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());
    }
10
  • 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. Commented Mar 28, 2023 at 11:07
  • Thats a great suggestion but no luck :/ Commented Mar 28, 2023 at 12:47
  • Did you try it without that (rather suspicious looking) build_post_fields method? Passing $body directly? Commented Mar 28, 2023 at 12:53
  • yes I did @CBroe, Ill get an exception "Array to string conversion" Commented Mar 28, 2023 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? Commented Mar 28, 2023 at 13:06

1 Answer 1

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"
        ]
      ]
    ]
Sign up to request clarification or add additional context in comments.

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.