How to set multiple Content-Types? I have to pass an array to request body. That array contains a text and an image in binary.
curl_setopt($apiCon, CURLOPT_POSTFIELDS, array(
'status' => rawurlencode($status),
'photo' => $photoInBinary
));
curl_setopt($apiCon, CURLOPT_HTTPHEADER, array(
'Host: api.mixi-platform.com',
'Authorization: OAuth ' . $accessToken,
'Content-Type: multipart/form-data'
));
The problem is the host doesn't understand the format of the image, so I need to pass more content type 'image/jpg' but I don't know where to put it.
The above code works but it posts only the status.
Update:
Ok, my goal is to post a status with a photo to a social network page.
For more information, read this:
http://developer.mixi.co.jp/en/connect/mixi_graph_api/mixi_io_spec_top/voice-api/#toc-10
This is my code. It works but post only the status, not photo.
$apiCon = curl_init(self::API_POST_STATUS_WITH_PHOTO);
curl_setopt($apiCon, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($apiCon, CURLOPT_RETURNTRANSFER, true);
curl_setopt($apiCon, CURLOPT_POST, true);
curl_setopt($apiCon, CURLOPT_POSTFIELDS, array('status' => rawurlencode($status), 'photo' => $photoInBinary));
curl_setopt($apiCon, CURLOPT_HTTPHEADER, array('Host: api.mixi-platform.com', 'Authorization: OAuth ' . $accessToken, 'Content-Type: multipart/form-data'));
$result = curl_exec($apiCon);
curl_close($apiCon);
First try not encoding your status field (i.e., remove rawurlencode). This is a double-encode and possibly this is why your host is complaining.
(As an aside, you don't need to set the content-type header explicitly; CURL will do that.)
If this isn't enough, you either have to rearrange your request to use CURL's magic file upload mechanism, or you have to construct the entire multipart/form-data string by yourself.
This is how CURL's magic file mechanism works (from the documentation to CURLOPT_POSTFIELDS:
The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with # and use the full path. The filetype can be explicitly specified by following the filename with the type in the format ';type=mimetype'. This parameter can either be passed as a urlencoded string like 'para1=val1¶2=val2&...' or as an array with the field name as key and field data as value. If value is an array, the Content-Type header will be set to multipart/form-data. As of PHP 5.2.0, value must be an array if files are passed to this option with the # prefix.
The only way to pass the content-type of a specific field in a multipart/form-data POST using CURL is with this syntax for the field value: #filepath;type=mime/type.
If your $photoInBinary started life in a file, simply pass the filename in the above format to CURL instead of opening the file and reading in the data.
However, if you created the photo yourself in memory, you need to write that to a temporary file. Below is how you might do that:
function curlFileUploadData($data, $type='') {
// $data can be a string or a stream
$filename = tempnam(sys_get_temp_dir(), 'curlupload-');
file_put_contents($filename, $data);
$curlparam = "#{$filename}";
if ($type) {
$curlparam .= ";type={$type}";
}
return array($curlparam, $filename);
}
list($curlparam, $tmpfile) = curlFileUploadData('thedata', 'image/bmp');
// You can see the raw bits CURL sends if you run 'nc -l 9999' at a command line first
$c = curl_init('http://localhost:9999');
curl_setopt($c, CURLOPT_POSTFIELDS, array('status' => 'good', 'photo' => $curlparam));
curl_exec($c);
curl_close($c);
unlink($tmpfile); // REMEMBER TO DO THIS!!!!
Note that CURL will set the filename parameter on the multipart file upload. Be sure the host doesn't use this for anything important. As far as I know there's no way to override the filename CURL sends--it will always be exactly what was given.
If you are not willing to create a temporary file and you must use CURL, you will have to create the entire multipart/form-data body as a string in memory, and give that to CURL as a string to CURL_POSTFIELDS, and manually set the content-type header to multipart/form-data. This is left as an exercise for the reader. By this time you should consider using the HTTP extension instead, or even fopen with stream_context_create() to set the http method and headers.
Related
I'm trying to test a rest api for upload a local file to a https url. I am currently using curl to do it. The problem is that my POST in curl is getting converted into PUT (http://curl.haxx.se/docs/manpage.html) mentions the same.
To test from command line I'm using the following:
curl -H "Content-Type: application/json" -H "Authorization:Bearer XXXXXXXXXXXXXXXYYYYYzzzzzz" -H "Accept-Version: ~1" -H "Except: 100-continue" -H "Accept-Version: ~1 " -T "somefile.pdf" "https://abc-xyz.co/docs"
Output:
{"code":"BadMethod","message":"/docs does not support PUT"}
my php code:
$url = '"https://abc-xyz.co/docs';
$filepath = '/Users/me/Documents/somefile.pdf'; //mac path
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt($ch, CURLOPT_HTTPHEADER,
array('Authorization: Bearer XXXXXXXXXXXXXXXYYYYYzzzzzz', 'Accept-Version: ~1', 'Except: 100-continue');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
$file = "#".$filepath;
curl_setopt($ch, CURLOPT_POSTFIELDS,$file);
$response = curl_exec($ch);
if(!CURL_ERROR($ch))
{
echo"\n The output is:".$response;
}
else
{
echo "\nUpload failed ".curl_error($ch);
exit;
}
curl_close($ch);
Output: I get parse error.
Can some one educate me with 3 things:
Can I use curl to do POST for https? if yes, how? and if no what should my approach be?
How to pass the file path name in my php? I tried "#".$filepath too but it still doesn't like it.
If I want to restrict my file upload type to be only pdf should i be using mime type? (application/pdf????)
Thanks,
DR
From the commandline, see post fileupload with curl.
From PHP, see the manual
The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with # and use the full path. The filetype can be explicitly specified by following the filename with the type in the format ';type=mimetype'. This parameter can either be passed as a urlencoded string like 'para1=val1¶2=val2&...' or as an array with the field name as key and field data as value. If value is an array, the Content-Type header will be set to multipart/form-data. As of PHP 5.2.0, value must be an array if files are passed to this option with the # prefix.
curl_setopt($ch, CURLOPT_POSTFIELDS,array('someuploadname'=> "#".$filepath));
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?
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.
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/
I'm trying to upload a file on a form on my site, and then pass it on to a remote API.
This is my PHP:
$fields = array(
'file'=>$_FILES["mediaupload"],
'username'=>urlencode($_POST["username"]),
'password'=>urlencode($_POST["password"]),
'latitude'=>urlencode($_POST["latitude"]),
'longitude'=>urlencode($_POST["longitude"]),
);
$fields_string = http_build_query($fields);
$url = my_url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POST,count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec ($ch);
At the moment I keep getting error messages that the file could not be processed properly. The API expects all fields as POST strings except the file, which it expects in binary.
I know it's going to be tough to debug this for you guys without access to the remote API, but am I doing anything obviously wrong, or should this work?
Many thanks.
File upload using curl does not work like this. You need to first save the file locally using php's move_uploaded_file then get the path to file. In the fields add this,
$fields = array(
'file'=>"#/path/to/myfile.ext",
'username'=>urlencode($_POST["username"]),
'password'=>urlencode($_POST["password"]),
'latitude'=>urlencode($_POST["latitude"]),
'longitude'=>urlencode($_POST["longitude"]),
);
Also,
I'm not sure if fields array needs to be converted to string to be used as postfields. According to manual it can be an array() directly.
CURLOPT_POSTFIELDS The full data to
post in a HTTP "POST" operation. To
post a file, prepend a filename with #
and use the full path. The filetype
can be explicitly specified by
following the filename with the type
in the format ';type=mimetype'. This
parameter can either be passed as a
urlencoded string like
'para1=val1¶2=val2&...' or as an
array with the field name as key and
field data as value. If value is an
array, the Content-Type header will be
set to multipart/form-data. As of PHP
5.2.0, files thats passed to this option with the # prefix must be in
array form to work.
So I'm working on a script that's going to upload a video to a server via a RESTful interface. The documentation tells me that I should pass the data (including the binary video file) as part of a POST request. I know how to set my POST variables, but I'm not sure how to do the binary data. The API says I should have a field called 'media' and it should contain the raw video data.
So let's say I have a video called 'video1.mp4' and I want to include its contents in my 'media' POST variable. How can I do this?
Thanks!
I don't know how you're communication with the API, but I'll assume cURL for this example. To send files, you use the CURLOPT_POSTFIELDS option:
CURLOPT_POSTFIELDS
The full data to post in a HTTP "POST" operation. To post a file, prepend a filename with # and use the full path. This can either be passed as a urlencoded string like 'para1=val1¶2=val2&...' or as an array with the field name as key and field data as value. If value is an array, the Content-Type header will be set to multipart/form-data.
With an example further down on the page:
$ch = curl_init();
$data = array('name' => 'Foo', 'media' => '#/home/user/test.png');
curl_setopt($ch, CURLOPT_URL, 'http://localhost/upload.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_exec($ch);
Say I upload a file with PHP, CURL:
$postData = array();
$postData['file_name'] = "test.txt";
$postData['submit'] = "UPLOAD";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url );
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POST, 1 );
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData );
Now assume I have to manually set the content-length header.
$headers=array(
"POST /rest/objects HTTP/1.1",
'accept: */*',
"content-length: 0" //instead of 0, how could I get the length of the body from curl?
)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //set headers
$response = curl_exec($ch);
How would I measure the size of the body? (just specifying the filesize as content length doesn't seem to work)
In another example what if the body contains data that is not an actual file. (manually set by postfields) In that situation how would I get the content length of the body?
Thanks for any light shed on this, it appears to be a tough issue.
To get the length of a post body, try formatting the fields int a GET style string (aka param1=value1¶m2=value2) then setting that string as the CURL_POSTFIELDS with curl_setopt. An array does not have to be supplied. You can simply use strlen() to get the value to use for the content-length header.
If you are posting a file (or files) in addition to other fields, as you appear to be in the example above, you have to supply the value for the file as #/path/to/file, then get the filesize in bytes and add that to the total content-length.
So for the above example, assuming the file test.txt is in the /test dir of your server, the post value string would be file_name=#/test/text.txt&submit=UPLOAD. You MUST url_encode this string as well, before you assign it as the curl post value. To get the content length you get the length of that string (post url-encoding) and add it to the filesize of /test/test.txt.
That sounds wrong. The data generated will include sub-headers and Content-Length needs to also include those sub-headers. And since the sub-headers include a boundary, may include this and that data, this whole thing cannot work (the size cannot be specified without knowing exactly what the complete request is going to be.)
Actually, the only one that can compute the size, from what I can see is cURL itself. But it doesn't do it 8-P