RESTful uploading of a file in PHP - php

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&para2=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);

Related

Use cURL to call API in PHP

I have the following codes, however, it does not return anything-a blank page. But if I plug in the right parameters and place the entire link on web browser, it would return the results I want. I don't want to use file_get_contents because I need to use this function for other API calls that cannot return results by entering the entire link on the address bar. Thanks for helping.
<?php
$data_string ='<HotelListRequest><hotelId>A HOTEL</hotelId></HotelListRequest>';
// Tell CURL the URL of the recipient script
$curl_handle = curl_init ();
curl_setopt ($curl_handle, CURLOPT_URL, 'http://api.ean.com/ean-services/rs/hotel/v3/info?minorRev=4&cid=MYID&apiKey=MYAPIKEY&customerSessionId=&locale=en_US&currencyCode=USD&xml=');
// This section sets various options. See http://www.php.net/manual/en/function.curl-setopt.php
curl_setopt ($curl_handle, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt ($curl_handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($curl_handle, CURLOPT_POST, true);
curl_setopt ($curl_handle, CURLOPT_POSTFIELDS, $data_string);
// Perform the POST and get the data returned by the server.
$result = curl_exec ($curl_handle);
// Close the CURL handle
curl_close ($curl_handle);
echo $result;
?>
If you put everything into a browser and it works, you're using GET data. Does the service support the use of POST data (which is what you're sending in your example)? Have you tried sending all the data via the CURLOPT_URL?
Also, you'll want to change the data string to
"xml=".$data_string
and possibly you're other arguments as well (depending on the API).
According to http://php.net/manual/en/function.curl-setopt.php :
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&para2=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.
So you should modify you $data_string variable content to match the required format.

Parameters missing/damaged in PHP curl request

Update: I have solved the problem by using file_get_contents (or other file operations such as fopen) instead of curl, as is shown in this example: https://developers.google.com/chart/image/docs/post_requests. The question is, therefore, why curl is not able to POST the correct parameters while file operations can? This is essentially asking the difference between curl and file_get_contents...
The original problem
I'm using curl in PHP to download an image. The image is a Google Image Chart (https://developers.google.com/chart/image/) generated according to parameters passed through GET or POST to http://chart.googleapis.com/chart. Here is an example chart:
http://chart.googleapis.com/chart?cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World
The simple way is to GET this URL, and it works. However when I tried to GET a longer URL (with more parameters for the data of the chart), Google Chart returned a 400 page saying "malformed or illegal request". I pointed my browser to that longer URL and saw the desired chart, and thus could verify that the parameters in URL were correct.
Suspecting that the problem might be that the URL was too long, I used POST. Here is how I use POST to get the example chart (complete code, you may want to copy it and run):
$postData = array(
'chl' => 'Hello|World',
'chs' => '250x100',
'cht' => 'p3',
'chd' => 't:60,40',
'chof' => 'validate' // turn on the debugging mode
);
$ch = curl_init('http://chart.googleapis.com/chart');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
$ret = curl_exec($ch);
$info = curl_getinfo($ch);
echo $info["request_header"]; // check my sent header
echo $ret; // print out received data, expected: garbled text representing a PNG
curl_close($ch);
Then Google chart returned (even for this example chart) a 400 page saying that "The Chart API request contains no valid parameters." Notice the word "valid" is here, in contrast to "the Chart API request contains no parameters" upon receiving no parameters at all. Therefore Google Chart must have received some parameters but just that it cannot recognize them.

Send cUrl data without vars array

I try to post data without setting variable or array. I don't know it's possible ?
When i send $data = array('var_name'=>'var_val') everything works fine, but when i set $data ='to send' i don't get any post data.
$data = 'sample data to send';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
//curl_setopt($ch,CURLOPT_HTTPHEADER,array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$resp = curl_exec($ch);
From the manual:
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&para2=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.
Simple strings are assumed to be key/value pairs by the other end. Whatever the other end is doesn't see the un-keyed value you're passing. If this was a GET instead of a POST, I'd say just inspect the query string. As that isn't the case, you'll want to send the data with a key and a value instead, or figure out how the other end reads raw POST data. If the other end is PHP, there are at least two ways to do this.
The reason is "to send" becomes a key for $_POST data. To be able to see it, in the PHP file which is $url, do var_dump($_POST); This should show that key value on output. But as you can expect, it doesn't have any value.
You can take the data you have written in POSTFIELDS as raw with
$yourPostedData = file_get_contents('php://input');
POST request does not necessarily contain pairs of variable = value. Sometimes it contains raw data. To access it, you need to use the variable $HTTP_RAW_POST_DATA that will be filled with raw POST data in that case.

How to set multiple Content-Types?

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&para2=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.

PHP cURL: upload file from form to remote API?

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&para2=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.

Categories