Как загрузить файлы (multipart / form-data) с помощью многомерных POSTFIELDS с помощью PHP и CURL?

У меня возникают проблемы с отправкой многомерного массива с загрузкой файлов с использованием PHP и CURL.

Например, многомерный массив:

$post['question'] = 'Are you human?'; $post['answers'] = array('yes', 'no', 'maybe'); $post['file'] = '@/path/to/file'; // Output: Array( 'question' => Are you human?, 'answers' => Array( '0' => yes, '1' => no, '2' => maybe ), 'file' => @/path/to/file ) 

Есть несколько причин, почему это не сработает, если вы просто попытаетесь опубликовать это с помощью CURLOPT_POSTFIELDS в CURL следующим образом:

 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://example.com'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $response = curl_exec($ch); 

Прежде всего официальное описание PHP CURLOPT_POSTFIELDS гласит:

Полные данные для отправки в HTTP-режиме «POST». Чтобы опубликовать файл, добавьте имя файла с помощью @ и используйте полный путь. Это может быть либо передано как строка с urlencoded, как «para1 = val1 & para2 = val2 & …», или как массив с именем поля в качестве ключа и данных поля как значение. Если значением является массив, заголовок Content-Type будет установлен в multipart / form-data.

Похоже, вы можете передать любой тип массива POSTFIELDS правильно? Неправильно. POSTFIELDS принимает только нескалярные значения и будет подавлять с ошибкой Array to string conversion при передаче многомерных массивов. Таким образом, единственный другой вариант, который у вас есть, – это http_build_query() ваш массив, чтобы иметь возможность передавать многомерные массивы, которые не задыхаются.

Но … как вы можете прочитать в примечании на странице PHP:

Примечание. Передача массива в CURLOPT_POSTFIELDS будет кодировать данные как multipart / form-data, в то время как передача строки с кодировкой URL будет кодировать данные как application / x-www-form-urlencoded.

Сообщение не будет закодировано в multipart / form-data, если вы передадите строку urlencoded в POSTFIELDS, что приведет к сбою загрузки файла.

Таким образом, представляется невозможным комбинировать два с CURL, хотя это не было бы проблемой, если бы вы использовали обычную HTML-форму.

Мой вопрос: можно ли обойти эту странную причуду CURL, чтобы иметь возможность отправлять многомерные массивы и загружать файлы?

Related of "Как загрузить файлы (multipart / form-data) с помощью многомерных POSTFIELDS с помощью PHP и CURL?"

multipart / form-data не поддерживает вложенные значения. И я не верю, что CURL тоже это сделает.

Я подозреваю, что принимающая сторона вашего запроса также является скриптом PHP. Если, то вы можете представить вложенный массив как одно из значений, если вы просто подготовили его самостоятельно:

  $post['answers[0]'] = "yes"; $post['answers[1]'] = "no"; $post['answers[2]'] = "maybe"; 

Теоретически вам просто нужны 'answers[]' без индекса, но это перепишет предыдущее значение и, следовательно, будет работать только с http_build_query.

Я не уверен, есть ли какая-либо HTTP-библиотека в PHP, которая может сделать это автоматически.

Другой способ выполнить первый ответ:

 foreach( array("yes","no","maybe") as $key=>$value ) $post["answers[$key]"] = $value; 

Попробуйте эту рекурсивную функцию.

https://gist.github.com/yisraeldov/ec29d520062575c204be7ab71d3ecd2f

 <?php function build_post_fields( $data,$existingKeys='',&$returnArray=[]){ if(($data instanceof CURLFile) or !(is_array($data) or is_object($data))){ $returnArray[$existingKeys]=$data; return $returnArray; } else{ foreach ($data as $key => $item) { build_post_fields($item,$existingKeys?$existingKeys."[$key]":$key,$returnArray); } return $returnArray; } } 

И вы можете использовать его так.

 curl_setopt($ch, CURLOPT_POSTFIELDS, build_post_fields($postfields));