Как загрузить файлы (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" операция. Чтобы опубликовать файл, добавьте filename с @и использовать полный путь. Это может быть передано как urlencoded string like '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 будет кодировать данные как применение/х-WWW-форм-urlencoded.

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

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

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

3 ответа

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

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

$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));

licensed under cc by-sa 3.0 with attribution.