skip to Main Content

I have an API I am trying to use that accepts multiple files in a single HTTP POST via multipart form data.

The problem is, the files have the same key (images). In Postman, the sample request looks like so:
Postman example

And in cURL this works too:

curl --location 'http://my-amazing-service.local' 
--form 'images=@"/Users/zach/Desktop/1.jpg"' 
--form 'images=@"/Users/zach/Desktop/2.jpg"' 
--form 'images=@"/Users/zach/Desktop/3.jpg"'

The problem is in PHP, CURLOPT_POSTFIELDS does not take multidimensional arrays. For whatever reason, it works on my Mac I do anyway, but not inside a Docker container running Alpine Linux.

When I run the code below on Alpine Linux (in a Docker container), I can see from the Content-Length in the header is only 146, meaning the binary contents of the image are not being included (my test image is about 50KB).

Is it possible to pass multiple files into CURLOPT_POSTFIELDS? I’d rather not have to write the raw multipart POST body manually if possible.

$curl = curl_init();
      
curl_setopt_array($curl, array(
    CURLOPT_URL => 'http://my-amazing-service.local',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_POSTFIELDS => [
        'images' => [
            file_get_contents('1.jpg'),
            file_get_contents('2.jpg'),
            file_get_contents('3.jpg'),
        ],
    ]
));
$verbose = fopen('php://temp', 'w+');
curl_setopt($curl, CURLOPT_STDERR, $verbose);
curl_setopt($curl, CURLOPT_VERBOSE, true);
$response = curl_exec($curl);
if ($response === FALSE) {
    printf("cURL error: %sn", curl_error($curl));
}
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "Verbose information:n<pre>", $verboseLog, "</pre>n";        
curl_close($curl);    

2

Answers


  1. Chosen as BEST ANSWER

    After some more checking, it looks like the reason it's so hard to send a bunch of files in the same field name is because it technically violates RFC 2388:

    In forms, there are a series of fields to be supplied by the user who fills out the form. Each field has a name. Within a given form, the names are unique.

    So this problem just went from a "Can I do it?" to a "Should I do it?"

    I think the better solution is to reach out to the API maintainer and bring this up.


  2. You can use the function curl_file_create to attach multiple files on your request, e.g:

    $file1 = curl_file_create('1.jpg');
    $file2 = curl_file_create('2.jpg');
    
    $data = array(
      'file1' => $file1,
      'file2' => $file2
    );
    
    $ch = curl_init('https://example.com/upload.php');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    $response = curl_exec($ch);
    

    Using your approach:

    $curl = curl_init();
          
    curl_setopt_array($curl, array(
        CURLOPT_URL => 'http://my-amazing-service.local',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 0,
        CURLOPT_POSTFIELDS => [
            'file1'  => curl_file_create('1.jpg'),
            'file2' => curl_file_create('2.jpg'),
            'file3' => curl_file_create('3.jpg'),
        ]
    ));
    
    $response = curl_exec($curl);
    

    In order to use a single key on the CURLOPT_POSTFIELDS, you have to use the same syntax used to pass an array in a query string:

    curl_setopt_array($curl, array(
        CURLOPT_URL => 'http://my-amazing-service.local',
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 0,
        CURLOPT_POSTFIELDS => [
            'files[0]'  => curl_file_create('1.jpg'),
            'files[1]' => curl_file_create('2.jpg'),
            'files[2]' => curl_file_create('3.jpg'),
        ]
    ));
    

    This is the same syntax that we use to pass an array of values on a query string:

    http://example.com?name[0]=jon&name[1]=ari&name[2]=alex
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search