I realise I can do this with CURL very easily, but I was wondering if it was possible to use file_get_contents()
with the http stream context to upload a file to a remote web server, and if so, how?

- 19,824
- 17
- 99
- 186

- 12,298
- 15
- 60
- 87
-
1Don't you mean file_put_contents()? – mellowsoon Oct 23 '10 at 18:32
-
Nope. Try doing file_put_contents with a URL and you'll get "HTTP wrapper does not support writeable connections" – Shabbyrobe Oct 24 '10 at 01:01
-
1But with *file_get_contents()* you can download files, not upload!? – KingCrunch Nov 19 '10 at 13:10
-
How you can _write_ to a stream using *file_get_contents()*? :? – KingCrunch Nov 22 '10 at 16:19
-
3Actually, the HTTP stream wrapper always writes to the stream, because a HTTP call contains both a request (write) and a response (read). – netcoder Nov 22 '10 at 16:28
2 Answers
First of all, the first rule of multipart
Content-Type is to define a boundary that will be used as a delimiter between each part (because as the name says, it can have multiple parts). The boundary can be any string that is not contained in the content body. I will usually use a timestamp:
define('MULTIPART_BOUNDARY', '--------------------------'.microtime(true));
Once your boundary is defined, you must send it with the Content-Type
header to tell the webserver what delimiter to expect:
$header = 'Content-Type: multipart/form-data; boundary='.MULTIPART_BOUNDARY;
Once that is done, you must build a proper content body that matches the HTTP specification and the header you sent. As you know, when POSTing a file from a form, you will usually have a form field name. We'll define it:
// equivalent to <input type="file" name="uploaded_file"/>
define('FORM_FIELD', 'uploaded_file');
Then we build the content body:
$filename = "/path/to/uploaded/file.zip";
$file_contents = file_get_contents($filename);
$content = "--".MULTIPART_BOUNDARY."\r\n".
"Content-Disposition: form-data; name=\"".FORM_FIELD."\"; filename=\"".basename($filename)."\"\r\n".
"Content-Type: application/zip\r\n\r\n".
$file_contents."\r\n";
// add some POST fields to the request too: $_POST['foo'] = 'bar'
$content .= "--".MULTIPART_BOUNDARY."\r\n".
"Content-Disposition: form-data; name=\"foo\"\r\n\r\n".
"bar\r\n";
// signal end of request (note the trailing "--")
$content .= "--".MULTIPART_BOUNDARY."--\r\n";
As you can see, we're sending the Content-Disposition
header with the form-data
disposition, along with the name
parameter (the form field name) and the filename
parameter (the original filename). It is also important to send the Content-Type
header with the proper MIME type, if you want to correctly populate the $_FILES[]['type']
thingy.
If you had multiple files to upload, you just repeat the process with the $content bit, with of course, a different FORM_FIELD
for each file.
Now, build the context:
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => $header,
'content' => $content,
)
));
And execute:
file_get_contents('http://url/to/upload/handler', false, $context);
NOTE: There is no need to encode your binary file before sending it. HTTP can handle binary just fine.

- 66,435
- 19
- 125
- 142
-
-
2That worked beautifully. Is there any chance you could also add to your answer how to add extra post fields with the upload? Also, is it possible to do multiple this way just by tacking on another boundary? – Shabbyrobe Nov 24 '10 at 03:42
-
Sure. For the POST fields, see my updated answer. As for your second question, I'm not sure what you mean. – netcoder Nov 24 '10 at 04:06
-
Thanks for the update. I just meant could I upload two files at once by tacking on another one of these: $content = "--".MULTIPART_BOUNDARY."\r\n". "Content-Disposition: form-data; name=\"form_field_2\"; filename=\"".basename($otherFileName)."\"\r\n". "Content-Type: image/jpeg\r\n\r\n". $otherFileContents."\r\n"; – Shabbyrobe Nov 24 '10 at 04:48
-
-
Could anyone explain what the `name=\"foo\"\r\n\r\n"."bar\r\n";` part? Wouldn't it just be `foo=bar`? I'm very confused on that part. – bryan Feb 14 '15 at 04:33
-
@bryan: It's the equivalent to `foo=bar` but in the `multipart/form-data` format. – netcoder Feb 14 '15 at 14:24
-
Note: The boundary must be prefixed with "--" in the body but not in the head. This might save you the trouble that I had with my http request being invalid. – kosinix Oct 27 '15 at 04:34
-
@kosinix FYI it's already the case in the above answer. If you follow it as is, it will work – netcoder Oct 27 '15 at 04:46
-
2What if the file you want to send is very large, is there no way of sending chunks of data? – MacWise May 20 '16 at 17:38
-
Just FYI, sending data as chunked is a completely different topic. You'll have to use the chunked transfer encoding for that one, and this question and answer do not describe that protocol and weren't meant to. – netcoder Feb 04 '21 at 05:26
Or maybe you can just do :
$postdata = http_build_query(
array(
'var1' => 'some content',
'file' => file_get_contents('path/to/file')
)
);
$opts = array('http' =>
array(
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);
You'd change the '/path/to/file' to the appropriate path

- 336
- 1
- 5
- 15