I was recently working on a project where I had to send and receive binary files to and from a REST API, so I decided to document some of the code I wrote. Keep in mind that I’m extracting all of these code snippets from a custom PHP class I wrote, so if you’re working with an API, I’d encourage you to create a class of your own.
Setup a form to submit a file
Before you can send a binary file to an API, you have to get it from somewhere. In my project that file came from a form. The form could be as simple as this:
1 2 3 4 | <form method="post" action="index.php" enctype="multipart/form-data"> <input name="file" type="file" /> <input type="submit" value="Upload" /> </form> |
<form method="post" action="index.php" enctype="multipart/form-data"> <input name="file" type="file" /> <input type="submit" value="Upload" /> </form>
Note the enctype
attribute is set to multipart/form-data
. You must have this set, otherwise the file will not be submitted.
Grab the submitted file
Normally when you process a form, the contents are found inside of the global $_POST
variable, however file data is located within the global $_FILES
variable.
If you print out the contents of $_FILES
on the page the form was submitted to, you’d see something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | Array ( [file] => Array ( [name] => Array ( [0] => 400.png ) [type] => Array ( [0] => image/png ) [tmp_name] => Array ( [0] => /tmp/php5Wx0aJ ) [error] => Array ( [0] => 0 ) [size] => Array ( [0] => 15726 ) ) ) |
Array ( [file] => Array ( [name] => Array ( [0] => 400.png ) [type] => Array ( [0] => image/png ) [tmp_name] => Array ( [0] => /tmp/php5Wx0aJ ) [error] => Array ( [0] => 0 ) [size] => Array ( [0] => 15726 ) ) )
What’s unusual about this array is that instead of grouping each file’s data in one array, each piece of information about a file is grouped together. So the values at index 0
in name
, type
, tmp_name
, error
and size
belong to one file.
The binary file data is located in a temporary storage on your server. In this case, at /tmp/php5Wx0aJ
.
Send the binary contents via cURL
The following snippet sends the binary file to a URL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $url = 'http://some-server.com/api'; $header = array('Content-Type: multipart/form-data'); $fields = array('file' => '@' . $_FILES['file']['tmp_name'][0]); $token = 'NfxoS9oGjA6MiArPtwg4aR3Cp4ygAbNA2uv6Gg4m'; $resource = curl_init(); curl_setopt($resource, CURLOPT_URL, $url); curl_setopt($resource, CURLOPT_HTTPHEADER, $header); curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1); curl_setopt($resource, CURLOPT_POST, 1); curl_setopt($resource, CURLOPT_POSTFIELDS, $fields); curl_setopt($resource, CURLOPT_COOKIE, 'apiToken=' . $token); $result = json_decode(curl_exec($resource)); curl_close($resource); |
$url = 'http://some-server.com/api'; $header = array('Content-Type: multipart/form-data'); $fields = array('file' => '@' . $_FILES['file']['tmp_name'][0]); $token = 'NfxoS9oGjA6MiArPtwg4aR3Cp4ygAbNA2uv6Gg4m'; $resource = curl_init(); curl_setopt($resource, CURLOPT_URL, $url); curl_setopt($resource, CURLOPT_HTTPHEADER, $header); curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1); curl_setopt($resource, CURLOPT_POST, 1); curl_setopt($resource, CURLOPT_POSTFIELDS, $fields); curl_setopt($resource, CURLOPT_COOKIE, 'apiToken=' . $token); $result = json_decode(curl_exec($resource)); curl_close($resource);
Let’s talk about each of these lines, because some of this will depend on the requirements of the API you are using and may not be necessary.
$url
is where cURL will post your data.$header
tells the API that there is a file coming its way. You can supply more headers by adding additional values to the array.$fields
contains an associative array. The field name depends on what the API expects it to be. The@
sign in the value tells cURL that we’re dealing with a file.$token
is a token that the REST API expects as a cookie. Usually you login to the API once during a transaction, then use a token for all subsequent requests. The cookie is calledapiToken
, but this may differ depending on the API’s expectations.CURLOPT_RETURNTRANSFER
tells cURL we want a response.CURLOPT_POST
tells cURL we want our data to be posted.CURLOPT_COOKIE
sets the token as a cookie.
Lastly, notice the last line:
1 | $result = json_decode(curl_exec($resource)); |
$result = json_decode(curl_exec($resource));
In my case I know that the response I’m getting from the API will be in the JSON format, which is why I’m decoding it. You may not need this if you’re getting back a response in XML, for example.
This completes sending a binary file via cURL. Next we’ll look at retrieving that file.
Retrieve the binary contents via cURL
Here’s how to retrieve a binary file:
1 2 3 4 5 6 7 8 | $resource = curl_init(); curl_setopt($resource, CURLOPT_URL, $url); curl_setopt($resource, CURLOPT_HEADER, 1); curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1); curl_setopt($resource, CURLOPT_BINARYTRANSFER, 1); curl_setopt($resource, CURLOPT_COOKIE, 'apiToken=' . $token); $file = curl_exec($resource); curl_close($resource); |
$resource = curl_init(); curl_setopt($resource, CURLOPT_URL, $url); curl_setopt($resource, CURLOPT_HEADER, 1); curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1); curl_setopt($resource, CURLOPT_BINARYTRANSFER, 1); curl_setopt($resource, CURLOPT_COOKIE, 'apiToken=' . $token); $file = curl_exec($resource); curl_close($resource);
We don’t have to go over anything except for:
CURLOPT_HEADER
tells cURL that we expect there to be a header. This is important because it tells us what kind of file we’re getting i.e. an image, a Word document, a PDF, etc.CURLOPT_BINARYTRANSFER
tells PHP that the result will contain binary data. Lots of people claim you don’t need this line. I say try it with and without, just to be sure.
Now you have the binary file with its headers stored in $file
.
Give the file back to the user
Let’s go over on how to give the file back to the user. You could either store the file on the server and give the user a path to the file, or you can just let PHP handle all that on the fly and simply prompt the user to save the file.
Here is how I did that:
1 2 3 4 5 6 7 8 9 10 11 | $file_array = explode("\n\r", $file, 2); $header_array = explode("\n", $file_array[0]); foreach($header_array as $header_value) { $header_pieces = explode(':', $header_value); if(count($header_pieces) == 2) { $headers[$header_pieces[0]] = trim($header_pieces[1]); } } header('Content-type: ' . $headers['Content-Type']); header('Content-Disposition: ' . $headers['Content-Disposition']); echo substr($file_array[1], 1); |
$file_array = explode("\n\r", $file, 2); $header_array = explode("\n", $file_array[0]); foreach($header_array as $header_value) { $header_pieces = explode(':', $header_value); if(count($header_pieces) == 2) { $headers[$header_pieces[0]] = trim($header_pieces[1]); } } header('Content-type: ' . $headers['Content-Type']); header('Content-Disposition: ' . $headers['Content-Disposition']); echo substr($file_array[1], 1);
- On line 1 I separate the header from the rest of the file.
- On line 2-8 I parse the header contents and put them in an array so I can easily reference the elements I need.
- On line 9 &10 I tell PHP what the content type is and what the file should be called.
- On line 11 I output all of the binary contents. Together with the header, it prompts the user to save the file (or displays it in the browser if it’s an image, for example).
I want to touch on the function on line 11: substr($file_array[1], 1);
In my case the binary file had an extra space at the beginning of the file. The substr
function removes that. I’m not sure whether that space came from the API or from the explode
on line 1, so if this doesn’t work for you, try changing that line to: echo $file_array[1];
Hopefully this info will shed some light on how to post and retrieve files in a binary format. If you have any questions, leave them in the comments below.
Hi ,
I want to fetch a image using curl api and want to show directly in the fancy box, but it is showing in binary format always.Even I have added header there.
The same code is working if I don’t use the fancybox.
Can you please help me.
Have you tried storing the image in a temporary file, so that you can embed it in the fancybox?
Hi,
Can you suggest me on how can I verify that my file is uploaded correctly to the server?
1) Can I send some kind of file checksum to the server using CURL? so that the server also calculates the checksum on received file and compares two checksums and reply with proper response code.
How to do it in CURL?
You can create a script on the server that expects two values: a checksum and a filename. Then you can post those two values using a cURL. The server will then calculate the checksum of the existing file and compare it to what you sent. If it matches, you can return something like
true
and if it doesn’t, you can returnfalse
(and optionally remove the file). So, the first request uploads the file, and the second request verifies it.Hello Ryan
I have the following requirement
Upload a jpg format image to jpegmini server and receive the compressed image as the response
Please refer to the below jpegmini documentation
Method: POST
URL: http://ec2-54-209-163-171.compute-1.amazonaws.com/api/v1/optimize
Body: The data of the source jpeg photo
Command:
curl -X POST -H ‘Content-Type: image/jpeg’ –data-binary @/tmp/input.jpg –output /tmp/output.jpg http://ec2-54-209-163-171.compute-1.amazonaws.com/api/v1/optimize
If we break down the above command
-X POST – specify http POST method
-H ‘Content-Type: image/jpeg’ – specify body content is a jpeg
–data-binary @/tmp/input.jpg – send /tmp/input.jpg as the data of the request
–output /tmp/output.jpg – store response in /tmp/output.jpg (instead of stdout)
Please note that the jpegmini URL is an AWS instance
I am stopping the instance when not using it
If you want me to run the instance please let me know
I want to make this cURL request and receive response using PHP
Please help me out
Have you tried your cURL command via command line and did it give you the response you’re looking for? If so, have you already tried converting it to PHP and were unsuccessful? If so, post the PHP code you tried in a Gist so I can take a look at it.
Hello,
As you define in your blog, I have same folllow the above steps to upload and download files using curl, but its not working anymore. Can you define by the examples. So I can understand easily.
Thanks in Advance
Manish Arora
I’ll need a bit more information. What specifically isn’t working and what kind of response are you getting? When you say it isn’t working anymore, was it working for you before?
Hello Ryan,
I use your code and as you say that download popup will display on client browser, but its not showing and display only the content of file.
So how to download file, Please describe it here.
Thanks in advance
Manish
First, I would echo out
Content-Type
andContent-Disposition
to ensure those are right. Second, have you tried echoing out$file_array[1]
without usingsubstr()
? Third, for testing, save the file to the disk, download it, and then open it to verify the file was transmitted back to you properly.Ryan,
Thanks for the informative post. I have been banging my head over this problem of uploading a form with file using curl but simply can’t figure out what is wrong with my code.
Here is my code:
But when I see my output from
curl_getinfo
, i see that content type is still “text/html”, where as all the docs I have seen suggest that, once you add a file to postfields, curl automatically converts the content type to “multipart/form-data”. I get the other form data but do not get file data where i am posting my data.Not sure what am I doing wrong here. Any ideas?
I am on php 5.5, so appending ‘@’ to the file path is not working, since it is deprecated.
@phpdeveloper
I have not used the CURLFile class, but your syntax looks right.
Try manually setting the header to
multipart/form-data
using theCURLOPT_HTTPHEADER
option:Also, with regard to you not getting the file, you’ve checked
$_FILES
and it was empty?Ryan,
I tried manually setting the content_type to “multipart/form-data” but it has no effect.
Like this:
Yes,
$_FILES
is empty.OK, so I setup a local environment, running PHP 5.5.12 and cURL 7.19.7, setup the following test:
Where
upload.php
contained the following:And I got the following output:
Give the code a try and see if you get a different result. If this doesn’t work, make sure image permissions are correct, try a different (perhaps smaller) image, and make sure PHP can write to the
tmp
directory. I’m basically thinking it’s something other than your code.PS: I noticed
myimge.jpg
was misspelled, so make sure that there is a file by that name (missing the letter ‘a’ in image).Ryan,
Thanks for your help.
Your code looks same as mine and yetl I get different result. I am wondering if it has to do with the framework I am using. I am using Kohana 3.3 and capturing the request object in my api to get the post variables. I am going to see if there is some issue on that front. Please let me know if you have any experience with Kohana.
Ryan,
I started from the beginning, deleted all my code. Then copied your code and tested it, and it worked. š Looks like I had something in the code which was making it not work right. Not sure what it was though.
Thanks a lot for your help.
Great, glad to hear (minus the part that we don’t know what actually caused the issue).
Ryan,
I hope you’re still monitoring this thread.
Thank you in advance for reviewing this!
I’m feeling pretty stupid about this and am willing to pay for somebody else’s smarts on a consulting deal.
I’m trying to write a receiver for the following curl command line:
curl -d @it20010002.xml -H “Content-Type: application/xml” https://upofx.proautomation.com/w2uploadtest.php > it20010002e.xml
Here is my sender code
$cfile,
“name” => “it20210001.xml”
);
$resource = curl_init();
curl_setopt($resource, CURLOPT_URL, $url);
// curl_setopt($resource, CURLOPT_HTTPHEADER, $header);
curl_setopt($resource, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($resource, CURLOPT_POST, 1);
curl_setopt($resource, CURLOPT_POSTFIELDS, $params);
$result = curl_exec($resource);
var_dump(curl_getinfo($resource));
echo “result=”;
var_dump($result);
curl_close($resource);
echo “——result=$result—–“;
?>
Here is my receiver code:
<?php
echo "inside w2uploadtest”;
$uploaddir = realpath(‘./’) . ‘/’;
$uploadfile = $uploaddir . basename($_FILES[‘file’][‘name’]);
echo ‘Here is what happens’;
if (move_uploaded_file($_FILES[‘file’][‘tmp_name’], $uploadfile)) {
echo “File is valid, and was successfully uploaded.\n”;
} else {
echo “Possible file upload attack!\n”;
}
echo ‘Here is some more debugging info:’;
print_r($_FILES);
echo “\n\n”;
print_r($_POST);
echo “returning back”;
?>
my variables returned are:
array (size=26)
‘url’ => string ‘https://upofx.proautomation.com/w2uploadtest.php’ (length=48)
‘content_type’ => null
‘http_code’ => int 0
‘header_size’ => int 0
‘request_size’ => int 0
‘filetime’ => int -1
‘ssl_verify_result’ => int 0
‘redirect_count’ => int 0
‘total_time’ => float 0.109
‘namelookup_time’ => float 0
‘connect_time’ => float 0.031
‘pretransfer_time’ => float 0
‘size_upload’ => float 0
‘size_download’ => float 0
‘speed_download’ => float 0
‘speed_upload’ => float 0
‘download_content_length’ => float -1
‘upload_content_length’ => float -1
‘starttransfer_time’ => float 0
‘redirect_time’ => float 0
‘redirect_url’ => string ” (length=0)
‘primary_ip’ => string ‘50.246.192.200’ (length=14)
‘certinfo’ =>
array (size=0)
empty
‘primary_port’ => int 443
‘local_ip’ => string ‘192.168.0.44’ (length=12)
‘local_port’ => int 59452
result=boolean false
——
result=
—–
postResult=
Hi Brian,
Can you describe what you’re trying to accomplish and what issue you’re running into?
Hi Ryan,
I am trying to use the PUT method to send 2 files to a REST API. I am using following code:
The server is generating error.
Please, guide
Hi Ryan,
Thank you for your brief explanation. It has saved my day. My team mates were having so many issues but your post which I have found saved them a lot. We are very thankful to you.
Regards.
Thank you
Thank you very much. You’re amazing. It works as expected.
Hii Sir
Need to help to me
In Send file Successfully in HTTP ” POST ” request
But it’s not work in ” PUT ”
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, “PUT”);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
can you let me know
How to send file in HTTP ” PUT” method
and How to to get it on server side.
Thank you
I am trying to make code for download file into browser, my data is in db is uploaded as binary file using upload functionality.. I am able to download files like .ppt,.txt,.doc but image files are not opening after downloading.. Can any one please help?
Thank you so much man, the binary transfer option was missing in my code, and that got me stuck for about 3 hours until i came here. I finally connected to my api and succesfully got a response, i dont have words to thank you. Greetings!
Haha, Iām glad that this post is still helpful after all these years.
Hi, I am trying to post an image to xero invoice. Its working from postman. I have the $filename. Also i have the file path were file exist.
$filename = “01.jpg”; // (same folder as in php file)
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => “https://api.xero.com/api.xro/2.0/Invoices/55c2f140-45a0-4f90-b5a0-8d650bddae07/Attachments/01.jpeg”,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => “”,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => “PUT”,
CURLOPT_POSTFIELDS => “”,
CURLOPT_HTTPHEADER => array(
“Content-Type: image/jpeg”,
“Accept: application/json”,
“Authorization: Bearer $token
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
How do i pass that to postfields? In postman it is working. But i think postman is sending file in binary format. Please help