php cURL multiple file upload nightmare
I cannot believe it! I’ve spent all day on this annoying little deficiency (of cURL php library) that wouldn’t let me POST multiple files to remote API.
Unsuspecting me was hauling this array (showing json below, I don’t like printing arrays) around and wondering why files were not saved on the remote server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
"photos": [ { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-IGZEMF", "mime": "image\/png", "postname": "1" }, { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-7yFn2e", "mime": "image\/png", "postname": "2" }, { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-Pgi84t", "mime": "image\/png", "postname": "3" } ] |
My curl method…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
private function addCar( $dataArray ) { $this->info( 'Adding a car...' ); $ch = curl_init(); curl_setopt( $ch, CURLOPT_URL, $this->addCarUrl ); curl_setopt( $ch, CURLOPT_POST, true ); curl_setopt( $ch, CURLOPT_POSTFIELDS, $dataArray ); curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); curl_setopt( $ch, CURLOPT_COOKIEFILE, $this->cookiePath ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $ch, CURLOPT_VERBOSE, false ); $response = curl_exec( $ch ); curl_close( $ch ); $this->info( $response ); return $response; // 1 or 0 } |
Well, can you guess what the problem is?
Even though php docs say that CURLOPT_POSTFIELDS supports arrays, don’t believe it, it doesn’t really… well it accepts simple array key=>value pairs, but not multidimensional arrays!
So array above will spit out following: Array to string conversion exception.
So how do I submit multiple photos to API which expects photos[] ?
Firs thing that came to mind is of course: http_build_query(query_data)
1 |
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $dataArray ) ); |
Horrible idea!!
Because when you use string in CURLOPT_POSTFIELDS content type is set to Content-Type: application/x-www-form-urlencoded and no files are actually sent to API!
So dilemma: string -> cannot send file but can have photos[]; array -> can send 1 file but cannot have photos[]. What the hell right? How come no one thought of this?
My solution is a bit of a hack, and only works if you have php on the receiving end. Instead of generating a multidimensional array, we create simple key=>value pairs of photos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
foreach ( $carRaw[ 'imgs' ] as $k => $v) { //$v - contains img urls $tmpFile = tempnam( '/tmp/curlfiles', 'autoge-' ); file_put_contents( $tmpFile, file_get_contents( 'http://images.autoveli.ge/' . $v[ 'ipt' ] . '/im/im-' . $v[ 'ikey' ] ) ); $car[ "photos[ $k ]" ] = new \CURLFile( $tmpFile, 'image/png', $v[ 'ikey' ] ); } |
Notice this last part $car[ "photos[ $k ]" ]
This generates following 1 dimensional array that curl can accept!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
"photos[ 0 ]": { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-V0i48i", "mime": "image\/png", "postname": "4205238887411134296.jpg" }, "photos[ 1 ]": { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-zfp5Q2", "mime": "image\/png", "postname": "6341524831159418187.jpg" }, "photos[ 2 ]": { "name": "\/private\/var\/folders\/51\/pz1925r15pj7s3pqn9yxcngw0000gn\/T\/autoge-niD5oc", "mime": "image\/png", "postname": "4154430187984323116.jpg" } |
On the server php, receives this as "photos":{"0 ":{},"1 ":{},"2 ":{}}
Files are sent with correct content type and there is peace in the world again.
Leave a Reply