Viewed   651 times

I am trying to post an image with cURL in PHP using multipart/form-data header since API that I am sending to is expecting image to be sent as multi-part form.

I don't have problems talking to the API with other requests; only posting an image is an issue.

I am using this form on client side:

<form action="http://myServerURL" method="POST" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="Submit">
</form>

and this is the server I am posting to (here I am trying to post this data forward to an API):

$ch = curl_init($url);
curl_setopt ($ch, CURLOPT_POST, 1);
curl_setopt ($ch, CURLOPT_POSTFIELDS, $imgRawData); // <-- raw data here hm?
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt ($ch, CURLOPT_COOKIEFILE, $cookieJar);
curl_setopt( $ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1); <-- using this as I wanted to check if HTTPHEADER is set
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: multipart/form-data')); <-- setting content-type header?
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);

// i get response from the server
$response = curl_exec( $ch );

// with this I can check what kind of content type the last request had?
$requestContentType = curl_getinfo($ch,CURLINFO_CONTENT_TYPE);
echo "<br>request Content Type was:".$requestContentType."<br>";    

curl_close($ch);

echo "<br><b>SERVER POST IMAGE RESPONSE:</b><br>";
echo $response;

With the code below I am able to see my request headers:

curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, true);

var_dump(curl_getinfo($ch));

The content-type in request-headers is shown correctly now. But it seems the image is not send correctly as API would expect. Unfortunately I don't have access to the API...

Any help appreciated, thank you

 Answers

5

In case anyone had the same problem: check this as @PravinS suggested. I used the exact same code as shown there and it worked for me perfectly.

This is the relevant part of the server code that helped:

if (isset($_POST['btnUpload']))
{
$url = "URL_PATH of upload.php"; // e.g. http://localhost/myuploader/upload.php // request URL
$filename = $_FILES['file']['name'];
$filedata = $_FILES['file']['tmp_name'];
$filesize = $_FILES['file']['size'];
if ($filedata != '')
{
    $headers = array("Content-Type:multipart/form-data"); // cURL headers for file uploading
    $postfields = array("filedata" => "@$filedata", "filename" => $filename);
    $ch = curl_init();
    $options = array(
        CURLOPT_URL => $url,
        CURLOPT_HEADER => true,
        CURLOPT_POST => 1,
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_POSTFIELDS => $postfields,
        CURLOPT_INFILESIZE => $filesize,
        CURLOPT_RETURNTRANSFER => true
    ); // cURL options
    curl_setopt_array($ch, $options);
    curl_exec($ch);
    if(!curl_errno($ch))
    {
        $info = curl_getinfo($ch);
        if ($info['http_code'] == 200)
            $errmsg = "File uploaded successfully";
    }
    else
    {
        $errmsg = curl_error($ch);
    }
    curl_close($ch);
}
else
{
    $errmsg = "Please select the file";
}
}

html form should look something like:

<form action="uploadpost.php" method="post" name="frmUpload" enctype="multipart/form-data">
<tr>
  <td>Upload</td>
  <td align="center">:</td>
  <td><input name="file" type="file" id="file"/></td>
</tr>
<tr>
  <td>&nbsp;</td>
  <td align="center">&nbsp;</td>
  <td><input name="btnUpload" type="submit" value="Upload" /></td>
</tr>

Sunday, October 9, 2022
1

When you tell cURL to upload a file, the Content-Type header is automatically set to multipart/form-data. For a request like in your documentation, you would have to upload the file manually.

<?php
$url     = 'https://hostserver.com/gateway/remote_send';
$payload = array(
    'profile_name'  => 'username',
    'profile_pw'    => 'password1234',
    'attached_type' => 'action_1'
);
$file = realpath('/home/username/tests/test1234qwerty.csv');

// build multipart
$payload = http_build_query($payload);
$params  = "--ABC1234rn"
    . "Content-Type: application/x-www-form-urlencodedrn"
    . "rn"
    . $payload . "rn"
    . "--ABC1234rn"
    . "Content-Type: text/csvrn"
    . "Content-Disposition: attachment; filename="attachment.csv"rn"
    . "rn"
    . file_get_contents($file) . "rn"
    . "--ABC1234--";

$first_newline      = strpos($params, "rn");
$multipart_boundary = substr($params, 2, $first_newline - 2);
$request_headers    = array();
$request_headers[]  = 'Content-Length: ' . strlen($params);
$request_headers[]  = 'Content-Type: multipart/x-api-remote-integration; boundary='
    . $multipart_boundary;

// send the request now

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);

$reply = curl_exec($ch);

Does that work?

Thursday, September 1, 2022
 
4

I suggest you make an array like this:

$time  = (string) (time() - (100 * rand(1,6)));
$photo = file_get_contents($path);

$fields = array(
    array(
        'headers' => array(
            'Content-Disposition' => 'form-data; name="device_timestamp"',
            'Content-Length'      => strlen($time)
        ),
        'body' => $time
    ),
    array(
        'headers' => array(
            'Content-Disposition' => 'form-data; name="photo"; filename="photo"',
            'Content-Type'        => 'image/jpeg',
            'Content-Length'      => strlen($photo)
        ),
        'body' => $photo
    )
);

The method can then look like this:

private function multipart_build_query($fields)
{
    $data = '';

    foreach ($fields as $field) {
        // add boundary
        $data .= '--' . $this->boundary . "rn";

        // add headers
        foreach ($field['headers'] as $header => $value) {
            $data .= $header . ': ' . $value . "rn";
        }

        // add blank line
        $data .= "rn";

        // add body
        $data .= $field['body'] . "rn";
    }

    // add closing boundary if there where fields
    if ($data) {
        $data .= $data .= '--' . $this->boundary . "--rn";
    }

    return $data;
}

You now have a very generic method which supports any kind of field.

Saturday, August 20, 2022
 
4

It depends on what you mean by "more efficient". If your measure is time, then it can be more efficient.

The technique you're referring to is using a data URI. Typically you take the image data and base64 encode it so it contains only ASCII characters. base64 encoding data has the effect of making it 33% larger (every 6 bits become 8).

So this works for small images, but for large ones, the 33% premium may be too much.

The reason why it might be a good idea is that latency is often the limiting factor for browser requests. It used to be (back in the day) that bandwidth was the restriction, so the common advice was to split up your resources, but that's not true anymore. With a data URI image, the browser doesn't have to make a second round trip.

Aside from all that, you have to consider browser support. Prior to version 8, IE has no support for data URIs. And in IE 8, there's an upper limit of 32KB of data.

Hope this helps!

Sunday, October 2, 2022
 
15

Notepad will often corrupt binary files, as it is only intended to edit text files. Just open and save a good PNG file and you will afterward find it unreadable (messing with CR and LF characters, stopping at the first null byte, etc.). You may be interested in a hex editor. I have Frhed on my computer, but there are many alternatives. And you might want to read about the PNG file format so that you know, for example, where the beginning and end of the file are.

Did you know that automatic "file carving" programs exist? One such program is PhotoRec. You can drag your database file to that program and have it look for all the PNG files for you. The next step would be to run it on an old hard drive to find deleted files, which are only removed from the operating system's list of files and not actually erased until other files take their place. Your luck depends on how fragmented the files are, but it is likely you will find a lot (including cached web pages and old virus infections).

That, by the way, is why you should always not merely reformat but completely overwrite or destroy a computer's hard drive before disposing of it.

Saturday, October 8, 2022
 
hazeim
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :