0

So I've to integrate with an API that accepts files and a field called "fileMetadata" which is a json. The curl looks like this

curl --location 'HIDDEN-URL' \
--header 'X-upload-Type: batch' \
--header 'X-AUTH-TOKEN: HIDDEN-TOKEN' \
--form 'fileMetadata="[{\"fileName\":\"G1-Entry-L2-Back+2023-03-28_01_00_00.175.jpg\",\"deviceId\":\"D-TR-0010\",\"date\":\"20230328\",\"time\":\"120000\"}]";type=application/json' \
--form 'file=@"/PATH-TO-IMG/G1-Entry-L2-Back+2023-03-28_01_00_00.175.jpg"' \
--form 'file=@"/PATH-TO-IMG/G1-Entry-L2-Back+2023-03-28_01_00_00.175.jpg"'

I can set the fileMetadata content type to json in postman like this: postman

And the API won't accept the request unless its like that.

In my code, I did this:

        $headers = array(
        'X-AUTH-TOKEN: ' . config('services.camera.X-AUTH-TOKEN'),
        'X-upload-Type: batch'
    );
    $body = [];
    $local = Storage::disk('public');
    foreach($images as $key => $img){
        /* try and catch file download */
        //Downloading the file from a remote FTP and putting it in a temp file (So I can use it in curl)
        $local->put(".temp/" . $img['fileName'], Storage::disk("ftp")->get("new/" . $img['fileName']));
        $path = $local->path('.temp/' . $img['fileName']);
        //creating curl file
        $body['file'][$key] = curl_file_create($path, mime_content_type($path), basename($path));
    }
    // json encoding the file array details
    $body["fileMetadata"] = json_encode($images);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, config('services.camera.url') . $batchId);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $this->build_post_fields($body));
    $result = curl_exec($ch);
    curl_close($ch);
    $local->deleteDirectory('.temp');
    dd($result);

I used this solution for building the post field https://stackoverflow.com/a/41144063/10321719 (because it kept giving me "Array to string conversion" error)

But it still isn't working and the API is returning bad request, I'm pretty sure because its not seeing the json from fileMetadata field as I tried it in postman without specifying "fileMetadata" content type and it returned the same error.

CURL info:

"url" => "HIDDEN"
        "content_type" => "application/json"
        "http_code" => 400
        "header_size" => 296
        "request_size" => 294
        "filetime" => -1
        "ssl_verify_result" => 0
        "redirect_count" => 0
        "total_time" => 0.525641
        "namelookup_time" => 0.075162
        "connect_time" => 0.153136
        "pretransfer_time" => 0.260049
        "size_upload" => 760.0
        "size_download" => 151.0
        "speed_download" => 287.0
        "speed_upload" => 1445.0
        "download_content_length" => -1.0
        "upload_content_length" => 760.0
        "starttransfer_time" => 0.260055
        "redirect_time" => 0.0
        "redirect_url" => ""
        "primary_ip" => "HIDDEN"
        "certinfo" => []
        "primary_port" => 443
        "local_ip" => "HIDDEN"
        "local_port" => 56168
        "http_version" => 3
        "protocol" => 2
        "ssl_verifyresult" => 0
        "scheme" => "HTTPS"
        "appconnect_time_us" => 259936
        "connect_time_us" => 153136
        "namelookup_time_us" => 75162
        "pretransfer_time_us" => 260049
        "redirect_time_us" => 0
        "starttransfer_time_us" => 260055
        "total_time_us" => 525641

The part of code that calls upload function

        try {
        $files = Storage::disk('ftp')->files("/new");
        $unique_devices = [];
        foreach($files as $file){
            //breaking down the file name because it contains the API information we need
            $break = explode("+", $file);
            $device_id = str_replace("new/", "", $break[0]); // device id
            if(!isset($break[1])){
                continue;
            }
            if(!in_array($device_id, $unique_devices)){
                // The api integration wants the user to generate a batch for each unique camera so this is basically getting unique device ids in an array
                array_push($unique_devices, $device_id);
                $this->info[$device_id]["fileMetadata"] = [];
            }
            // breaking down other important information
            $file_name = str_replace("new/", "", $file);
            $dateBreakDown = explode(".", $break[1]);
            $date = explode("_", $dateBreakDown[0]);
            $time = $date[1] . $date[2] . $date[3];
            $date = str_replace("-", "", $date[0]);
            //pushing it into the array, basically this contains "images" variable from the upload funciton
            array_push($this->info[$device_id]["fileMetadata"], ["fileName" => $file_name, "deviceId" => "D-TR-0010"/*$device_id*/, "date" => $date, "time" => $time]);
        }
        $batches = $this->genBatchId($unique_devices);
        foreach($this->info as $dId => $upload){
            $this->uploadFiles($upload['fileMetadata'], $batches[$dId]);
        }
    } catch (Exception $e){
        Log::debug($e->getMessage());
    }
Tghosh
  • 180
  • 3
  • 14
  • I guess this should probably be possible using the `CURLStringFile` class - add the `fileMetadata` parameter using that, and specify `application/json` as the mime type. The second parameter, `postname`, can probably be left blank. – CBroe Mar 28 '23 at 11:07
  • Thats a great suggestion but no luck :/ – Tghosh Mar 28 '23 at 12:47
  • Did you try it without that (rather suspicious looking) `build_post_fields` method? Passing $body directly? – CBroe Mar 28 '23 at 12:53
  • yes I did @CBroe, Ill get an exception "Array to string conversion" – Tghosh Mar 28 '23 at 12:58
  • Not sure how you would manage that, since passing an array for CURLOPT_POSTFIELDS is commonplace and should work fine. Can you show the actual code you are using now? – CBroe Mar 28 '23 at 13:06
  • @CBroe the code is fully pasted in the question. I just didnt paste the function name. The function pretty much contains just that code in the question. I think the issue is from the API integration. But its doable only in postman so I'm not sure how can I replicate that. – Tghosh Mar 28 '23 at 13:14
  • All I need to do is pass an array of files, that are coming from an FTP, and a field called "fileMetadata" that contains metadata of the uploaded files to this api, but the API keeps sending 400 status code. – Tghosh Mar 28 '23 at 13:17
  • I edited my question for curl info if u wanna take a look. – Tghosh Mar 28 '23 at 13:30
  • I meant the modified code that actually tries to use CURLStringFile. _"and a field called "fileMetadata" that contains metadata of the uploaded files to this api"_ - so far I only see you encoding `$images`, but what exactly that contains, is unclear. Can't tell if it contains all the necessary fields, such as `deviceId`. Or maybe it contains extra stuff the API does not want. – CBroe Mar 28 '23 at 13:39
  • @CBroe sorry, my bad. This is the function that calls the upload function after getting the need information. (I edited my quesiton) – Tghosh Mar 28 '23 at 16:41

1 Answers1

0

I've solved this by using Guzzle instead of curl. You can specify the content type when using multipart like this

['name' => 'fileMetadata', 'contents' => $body['fileMetadata'], 'headers'  => ['Content-Type' => 'application/json']]

final multipart array will look like this

 array:2 [
      0 => array:3 [
        "name" => "file"
        "filename" => "G1-Entry-L2-Back+2023-03-26_15_18_00.092.jpg"
        "contents" => stream resource {@605
          timed_out: false
          blocked: true
          eof: false
          wrapper_type: "plainfile"
          stream_type: "STDIO"
          mode: "r"
          unread_bytes: 0
          seekable: true
          uri: "Path_to\storage\app/public\.temp/G1-Entry-L2-Back+2023-03-26_15_18_00.092.jpg"
          options: []
        }
      ]
      1 => array:3 [
        "name" => "fileMetadata"
        "contents" => "[{"fileName":"G1-Entry-L2-Back+2023-03-26_15_18_00.092.jpg","deviceId":"D-TR-0010","date":"20230326","time":"151800"}]"
        "headers" => array:1 [
          "Content-Type" => "application/json"
        ]
      ]
    ]
Tghosh
  • 180
  • 3
  • 14