40

I was wondering if it is possible to post a file - along with other form data - when the file is just a string?

I know that you can post a file that is already on the filesystem by prefixing the filepath with "@".

However I'd like to bypass creating a temporary file and send just the file as a string, but I am unsure how to construct the request using cURL in PHP.

Cheers

 $postFields = array( 'otherFields' => 'Yes' ,'filename' => 'my_file.csv' ,'data' => 'comma seperated content' ); $options = array( CURLOPT_RETURNTRANSFER => true ,CURLOPT_SSL_VERIFYPEER => false ,CURLOPT_SSL_VERIFYHOST => 1 ,CURLOPT_POSTFIELDS => $postFields ,CURLOPT_HTTPHEADER => array( 'Content-type: multipart/form-data' ) ); 
3
  • 1
    @Vin Definitely not a duplicate. This is a nice question. I don't know of any way to add part headers, as would be required here, short of manually crafting all the POST data. Commented Jun 21, 2010 at 15:37
  • @Artefacto: Right, I didn't understand the question correctly Commented Jun 21, 2010 at 15:59
  • 1
    by raw data do you mean file_get_contents($path_to_file); ? Commented Feb 18, 2011 at 4:20

3 Answers 3

29

Should be possible: here's a form, posted through a browser (irrelevant fields omitted):

POST http://host.example.com/somewhere HTTP/1.1 Content-Type: multipart/form-data; boundary=---------------------------7da16b2e4026c Content-Length: 105732 -----------------------------7da16b2e4026c Content-Disposition: form-data; name="NewFile"; filename="test.jpg" Content-Type: image/jpeg (...raw JPEG data here...) -----------------------------7da16b2e4026c Content-Disposition: form-data; name="otherformfield" content of otherformfield is this text -----------------------------7da16b2e4026c-- 

So, if we build the POST body ourselves and set an extra header or two, we should be able to simulate this:

// form field separator $delimiter = '-------------' . uniqid(); // file upload fields: name => array(type=>'mime/type',content=>'raw data') $fileFields = array( 'file1' => array( 'type' => 'text/plain', 'content' => '...your raw file content goes here...' ), /* ... */ ); // all other fields (not file upload): name => value $postFields = array( 'otherformfield' => 'content of otherformfield is this text', /* ... */ ); $data = ''; // populate normal fields first (simpler) foreach ($postFields as $name => $content) { $data .= "--" . $delimiter . "\r\n"; $data .= 'Content-Disposition: form-data; name="' . $name . '"'; // note: double endline $data .= "\r\n\r\n"; } // populate file fields foreach ($fileFields as $name => $file) { $data .= "--" . $delimiter . "\r\n"; // "filename" attribute is not essential; server-side scripts may use it $data .= 'Content-Disposition: form-data; name="' . $name . '";' . ' filename="' . $name . '"' . "\r\n"; // this is, again, informative only; good practice to include though $data .= 'Content-Type: ' . $file['type'] . "\r\n"; // this endline must be here to indicate end of headers $data .= "\r\n"; // the file itself (note: there's no encoding of any kind) $data .= $file['content'] . "\r\n"; } // last delimiter $data .= "--" . $delimiter . "--\r\n"; $handle = curl_init($url); curl_setopt($handle, CURLOPT_POST, true); curl_setopt($handle, CURLOPT_HTTPHEADER , array( 'Content-Type: multipart/form-data; boundary=' . $delimiter, 'Content-Length: ' . strlen($data))); curl_setopt($handle, CURLOPT_POSTFIELDS, $data); curl_exec($handle); 

This way, we're doing all the heavy lifting ourselves, and trusting cURL not to mangle it.

Sign up to request clarification or add additional context in comments.

9 Comments

Originally misread the question and got rightfully downvoted for it. Rewrote into something that could work.
I've constructed the request as Piskvor suggested and yes you can build the body yourself and it will work. Although you have to be 100% accurate with the EOLs. At the end of the "Content-Length" and "Content-Disposition" a double return is required. For the file after "Content-Type: text/plain" a double return is also required. The delimiter is also slightly different for the body values than that is decleared in the "Boundary". Each boundary line has two extra hyphens prepended to the begining. The last delimiter has two extra hypens at the begining and the end.
@Chris Henry: Indeed. I suggest that you read the question; quote: "I know that you can post a file that is already on the filesystem by prefixing the filepath with "@". However I'd like to bypass creating a temporary file..." So, your solution to "how to do this without @/some/file/name ?" is "use @/some/file/name"? The mind, it boggles...
When populating $postFields at the end of the first foreach you are missing $data .= $content . "\r\n"; otherwise the field content is not sent.
Thanks, @Piskvor! A note on the "name=" in the Content-Disposition line. It should be set to the name of the element that would have gotten the "@" file in the array given to CURLOPT_POSTFIELDS. If you set the file in curl_setopts CurlFile with $params->file = "@myfilename.pdf", then you'll want name="file". I suspect this is the same as a tag's "name" in HTML. Setting it to be the same as the file name might not be what the server side wants.
|
18

PHP has access to a temporary location "php://memory", which actually makes what you're trying to do fairly easy.

$fileHandle = fopen('php://memory', 'rw'); fwrite($fileHandle, $content); rewind($fileHandle); $options = array( CURLOPT_RETURNTRANSFER => true ,CURLOPT_HTTPHEADER => array( 'Content-type: multipart/form-data', ) ,CURLOPT_INFILE => $fileHandle ,CURLOPT_INFILESIZE => strlen($content) ); 

6 Comments

Will this allow for the rest of the postfields that are being sent with the file?
It definitely creates a file handler and I gave it a try, but I think CURLOPT_INFILE and CURLOPT_INFILESIZE are used to PUT files, No files showed up in the $_FILES array.
The code in the question can upload multiple files at once. Can PUT with INFILE upload multiple files too?
Why do you put comma at the start of lines? It's PHP, not C, you can just end all items with comma. [123, 456,]
@AlexP. because in 2010 php did not allow having comma after the last array element. They added this 'feature' some years later
|
1

PHP 8.1 has a new CURLStringFile class for this purpose:

$csv_content = 'comma,separated,content\nfoo,bar,baz'; $post_fields = array( 'file' => new CURLStringFile($csv_content, 'my_file.csv', 'text/csv') ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://example.com/'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.