1

I'm making a call to an external service using the Zend Http client. The service allows me to upload files to their storage system. It requires relevant params (userid, etc.) to be sent in the query string, and the file upload content should be sent in the POST body with a content-type of "application/zip" (I'm sending it a zip file with various things in it).

To do this, I set the params in the query string using the zend client's setParameterGet() function. I then set the file upload content using the setFileUpload() function:

$this->client->setFileUpload($zipFilePath, 'content', null, 'application/zip');

However, the service is telling me that I'm sending it the wrong content type, which is "multipart/form-data"

Here are the raw headers that the Zend client is sending to the service (note that I've removed bits of sensitive information, replacing them with item names enclosed in [] brackets)

POST https://[ServiceURL]?cmd=[COMMAND]&enrollmentid=[ENROLLMENTID]&itemid=[ITEMID]

HTTP/1.1

Host: [HOST] Accept-encoding: gzip, deflate

User-Agent: Zend_Http_Client Cookie:

AZT=9cMFAIBgG-eM1K|Bw7Qxlw7pBuPJwm0PCHryD;

Content-Type: multipart/form-data; boundary=---ZENDHTTPCLIENT-05535ba63b5130ab41d9c75859f678d8

Content-Length: 2967

-----ZENDHTTPCLIENT-05535ba63b5130ab41d9c75859f678d8

Content-Disposition: form-data; name="content"; filename="agilixContent.zip"

Content-Type: application/zip

[RAW FILE DATA HERE]

So basically, even though I've set the POST content type header, my external service is telling me I've sent the wrong content type, because there is another content-type header with the value "multipart/form-data". I've tried changing/removing that content header, but to no avail. How can I remove that header so that I won't have these two duplicate "content-type" headers in my requests?

dsw88
  • 4,400
  • 8
  • 37
  • 50
  • 1
    I'm looking over the ZF API for HTTP_Client (http://framework.zend.com/apidoc/1.11/), have you tried using "setEncType( string $enctype )", the API for setFileUpload is: "setFileUpload( string $filename, string $formname, string $data = null, string $ctype = null )", which I'd have to do some digging but it might not be what actually sets the overall header encoding. I'm digging over the Reference guide to see if there's any help there. – jmbertucci May 25 '12 at 18:32
  • I just tried using setEncType('application/zip') before I made the request, but the headers don't appear unchanged. The first one still says "multipart/form-data", while the second one says "application/zip". Thanks for suggesting that, I hadn't thought to try that function yet. :) – dsw88 May 25 '12 at 18:46
  • Hmm... I'm digging into the ZF code (Zend/Http/Client.php) and I'm doing a find on `multipart/form-data` and I'm seeing this comment `Force enctype to multipart/form-data` for the `setFileUpload()` function. this includes the `_prepareBody()` with `If we have files to upload, force enctype to multipart/form-data`. Long story short, it appears ZF_Http_Client won't let you set the overall packet content-type to `application/zip`. There might be a good reason for i but it might be due to the zip being an attachment and not just a single file. – jmbertucci May 25 '12 at 19:35
  • If you wanted to override it, create a new class and extend the client class and override that bit of code to allow. Though, I'll have to disclaimer that I'm not sure if that's a good suggestion as I'm not familiar enough with the HTTP protocol. – jmbertucci May 25 '12 at 19:38
  • Thanks for finding out about the client forcing to multipart/form-data when using setFileUpload! I did some tests, and it turns out that was my issue. Instead of using setFileUpload, I tried using setHeaders('Content-Type', 'application/zip') along with setRawData() to send the zip file content, and the "multipart/form-data" header stopped being sent with the request. The service is working normally now with that change. Thanks a bunch! – dsw88 May 25 '12 at 20:24
  • I'm speculating but it makes sense, the `setFileUpload()` method is like an email attachment. Sure you've got a zip file but really sending a multipart form, hench the reason it forces multipart. The `setRawData()` stream is sending the file itself, so it's not wrapping it in a multipart message. Glad you got that figured out. =) – jmbertucci May 25 '12 at 20:28

2 Answers2

2

If you want to upload a file using "application/zip" as content type you should not use ->setFileUpload() but rather ->setRawData(). setFileUpload() is used to mimick HTML form based file uploads which is not what you need.

See http://framework.zend.com/manual/en/zend.http.client.advanced.html#zend.http.client.raw_post_data for more information. What you need (based on your original example) will be something like:

$zipFileData = file_get_contents($zipFilePath);
$this->client->setRawData($zipFileData, 'application/zip');
$response = $this->client->request('POST');

Note that if your ZIP file may be very big (say more than a few megabytes) you may want to use ZHC's streaming support features, so avoid memory hogging. If you know your files are always less than 5-10 megabytes, I wouldn't bother with it though.

shevron
  • 3,463
  • 2
  • 23
  • 35
  • Yep, that turned out to be the issue. :) I didn't realize setFileUpload was mimicking uploading form fields. setRawData did the trick nicely. – dsw88 May 29 '12 at 20:04
0

I am not sure how you can do that with Zend HTTP Client but I am sure you could do that with plain cURL. As you must be knowing cURL gives you lot of flexibility and I have not dig in to Zend but there are chances that Zend might be using cURL internally.

<?php

// URL on which we have to post data
$url = "http://localhost/tutorials/post.php";
// Any other field you might want to catch
$post_data = "khan";
// File you want to upload/post
//$post_data['zip_file'] = "@c:/foobar.zip";

$headers[] = "Content-Type: application/zip";

// Initialize cURL
$ch = curl_init();
// Set URL on which you want to post the Form and/or data
curl_setopt($ch, CURLOPT_URL, $url);
// Data+Files to be posted
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
// Set any custom header you may want to set or override defaults
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 
// Pass TRUE or 1 if you want to wait for and catch the response against the request made
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// For Debug mode; shows up any error encountered during the operation
curl_setopt($ch, CURLOPT_VERBOSE, 1);
// Execute the request
$response = curl_exec($ch);

// Just for debug: to see response
echo $response;

I hope above snippet will work for you. That's bit modified code from my blog post mentioned below.

Reference: http://blogs.digitss.com/php/curl-php/posting-or-uploading-files-using-curl-with-php/

deej
  • 2,536
  • 4
  • 29
  • 51
  • fyi, I did test above code on my local machine and have got the right header for Content-Type as expected. – deej May 25 '12 at 18:43