Как загрузить файлы (multipart / form-data) с многомерными POSTFIELDS, используя PHP и CURL? - PullRequest
6 голосов
/ 11 августа 2010

У меня проблемы с публикацией многомерного массива с загрузкой файлов с использованием 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 закодировать данные как применение / х-WWW-форм-urlencoded.

Сообщение не будет закодировано в несколько частей / данных формы, если вы передадите строку с кодировкой urlen в POSTFIELDS, что приведет к сбою загрузки файла.

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

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

Ответы [ 3 ]

6 голосов
/ 11 августа 2010

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

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

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

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

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

2 голосов
/ 25 марта 2011

Еще один способ выполнить первый ответ:

foreach( array("yes","no","maybe") as $key=>$value )
    $post["answers[$key]"] = $value;
1 голос
/ 14 декабря 2016

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

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