Смешивание нескольких значений для одного и того же ключа и загрузки файлов с использованием cURL и PHP

Я столкнулся с ограничением в привязках cURL для PHP. Похоже, что нет простого способа отправить одни и те же несколько значений для одного и того же ключа для постполей. Большинство обходных путей, с которыми я столкнулся для этого, включают создание полей сообщений в кодировке URL вручную tag = foo & tag = bar & tag = baz) вместо использования версии CURLOPT_POSTFIELDS с ассоциативным массивом.

Это кажется довольно обычным делом - нуждаться в поддержке, поэтому я чувствую, что, должно быть, что-то упустил. Действительно ли это единственный способ обрабатывать несколько значений для одного и того же ключа?

Хотя этот обходной путь можно считать работоспособным (если не очень раздражающим), моя основная проблема заключается в том, что мне нужно иметь возможность делать несколько значений для одного и того же ключа, а также поддерживать загрузку файлов. Насколько я могу судить, загрузка файла более или менее требует использования версии CURLOPT_POSTFIELDS с ассоциированным массивом. Так что я чувствую, что застрял.

У меня есть опубликовал об этой проблеме более подробно в списке рассылки cURL PHP в надежде, что у кого-то есть идеи по этому поводу.

Мы очень ценим предложения или подсказки о том, где я могу найти дополнительную информацию по этому поводу!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
11
0
6 059
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Если в качестве имени вы используете tag[], а не tag, то PHP сгенерирует массив для вас, другими словами, а не

tag=foo&tag=bar&tag=baz

Тебе нужно

tag[]=foo&tag[]=bar&tag[]=baz

Обратите внимание, что при кодировании urlen-кода для передачи это должно стать

tag%5B%5D=foo&tag%5B%5D=bar&tag%5B%5D=baz

Я знаю, как создавать массивы в формах и URL-адресах для чтения PHP. Проблема в том, что я отправляю сообщения на сайт, отличный от PHP, который не использует хак []. Даже если это так, cURL не примет 'tag []' => array ('a', 'b', 'c') и создаст запрос, как вы предполагаете. Это проблема.

Beau Simensen 02.01.2009 04:54

Я думаю, что установленный стандарт для нескольких значений в одном ключе (или одном и том же ключе) состоит в том, чтобы объединить его с разделителем, например, для множественного выбора списков параметров в элементах формы. Я считаю, что этот разделитель - это символ табуляции (\t) или символ вертикальной черты (|).

Если имя ключа заканчивается [] (например, tag[]), PHP автоматически преобразует значения в массив для вашего удобства.

Я знаю, как заставить PHP принимать массив с тегом [], но проблема не в этом. Я отправляю сообщение на сайт без PHP, используя cURL, и мне нужно иметь возможность отправлять несколько значений для одного и того же ключа (тега), и я не мог заставить cURL сделать это, независимо от того, указываю ли я 'tag' или 'tag []' как имя параметра.

Beau Simensen 02.01.2009 04:56

Ммм, не уверен, что правильно понял. Вы отправляете сообщение в так называемую чью-то форму (что вполне нормально)? Разве исходная html-форма не допускает нескольких записей в этом элементе формы? Если нет, код-получатель этого не ожидает. Если да, скачайте wirehark и посмотрите, как он закодирован, затем воспроизведите

lImbus 03.01.2009 02:23

Я знаю, что они принимают несколько значений для одного и того же тега (tag = a & tag = b) и не используют формат tag [] = a & tag [] = b. Я создал автоматический загрузчик Google Code и считаю, что этот сайт написан на Python. Мне пришлось создать пользовательские поля для создания постинфо multipart / form-data.

Beau Simensen 04.01.2009 22:29

Кроме того, мое описание решения находится внизу списка. Хотел бы я принять свой ответ! Или, по крайней мере, поставьте на него несколько +1, чтобы он переместился наверх. Еще раз спасибо за вашу помощь, lImbus.

Beau Simensen 04.01.2009 22:30

Имбус и Пол, спасибо за ваш вклад.

Если бы у меня был контроль над формой, в которой я отправляю сообщение, я, вероятно, смог бы найти альтернативное решение этой проблемы. Однако я не контролирую форму. И я почти уверен, что программа, читающая этот пост, не является PHP и не подчиняется стандартам tag [].

Даже если это так, cURL, похоже, тоже не подчиняется синтаксису tag []. В принципе, я пробовал следующее, и ни один из них не работал ...

curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag[]' => array('a', 'b', 'c'));

curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@/pathtofile', 'tag' => array('a', 'b', 'c'));

И опять же, я не думаю, что передача tag [] сработает в любом случае, поскольку форма, в которую я отправляю сообщение, на самом деле ищет «tag», а не «tag []».

Я действительно начинаю чувствовать, что привязки cURL PHP действительно не поддерживают это. Что меня так удивляет. Кажется, что он может делать буквально все, что угодно, но не может делать что-то такое простое?

Ответ принят как подходящий

В итоге я написал свою собственную функцию для создания настраиваемой строки CURLOPT_POSTFIELDS с multipart / form-data. Вот это боль.

function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
    // $postfields is an assoc array.
    // Creates a boundary.
    // Reads each postfields, detects which are @files, and which values are arrays
    // and dumps them into a new array (not an assoc array) so each key can exist
    // multiple times.
    // Sets content-length, content-type and sets CURLOPT_POSTFIELDS with the
    // generated body.
}

Я смог использовать этот метод вот так:

curl_setopt_custom_postfields($ch, array(
    'file' => '@/path/to/file',
    'tag' => array('a', 'b', 'c'),
));

Я не уверен в стеках CURLOPT_HTTPHEADER, поэтому, поскольку этот метод вызывает его, я удостоверился, что функция позволит пользователю при необходимости указывать дополнительные заголовки.

У меня есть полный код в этом сообщении в блоге.

Я считаю, что это ответ на мой вопрос, но теперь я вижу способ на самом деле «ответить» на мой вопрос. :) Пожалуйста, проголосуйте за это, может быть, когда-нибудь кто-нибудь примет это за меня или что-то в этом роде?

Beau Simensen 01.01.2009 08:19

на данный момент вы можете принять свои собственные ответы, см. blog.stackoverflow.com/2009/01/accept-your-own-answers

lImbus 06.01.2009 16:29

Удивлен, что никто больше не сталкивался с этим, похоже, что-то, что должно быть частью библиотеки cURL php! Спасибо, что облегчили мне жизнь!

quickshiftin 18.11.2013 20:10

Я застрял в этой адской дыре в течение 3 дней сначала, я не знал, что я делаю неправильно, я понял только после создания формы и проверки запроса от инструментов разработчика в 2018 году, и это все еще не имеет правильного решения, кроме @ BeauSimensen, твой блог не работает, чувак

Buddhi741 06.02.2018 09:36

Веб-сайт не работает, вы можете увидеть функцию здесь yeehuichan.wordpress.com/2011/08/07/…

al37350 19.04.2019 13:13

Я получил его, используя:

curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));

тогда $_POST приводит к: $_POST['tag'][0] = 'val0' и $_POST['tag'][1] = 'val1'

Я столкнулся с той же проблемой. Но я смог решить это так.

for($cnt = 0; $cnt < count($siteRows); $cnt++)
{
    $curlParams['site_ids['.$cnt.']'] = $siteRows[$cnt]->site_id; 
}

Работает и с файлами:

for($cnt = 0; $cnt < count($imageRows); $cnt++)
{
    $curlParams['product_images['.$cnt.']'] = '@'.$imageRows[$cnt]->full_path;
}

Использует специфическую интерпретацию php имен полей сообщений, поэтому работает только в том случае, если ваша цель работает с php, но, поскольку это вопрос о написании клиентской части на php, я полагаю, что это вероятно. Мне помогло, +1

mbaynton 19.07.2015 01:42

Я нашел этот ответ в Интернете и хочу опубликовать его здесь, прежде чем он исчезнет:

http://yeehuichan.wordpress.com/2011/08/07/sending-multiple-values-with-the-same-namekey-in-curl-post/

function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
    $algos = hash_algos();
    $hashAlgo = null;
    foreach ( array('sha1', 'md5') as $preferred ) {
        if ( in_array($preferred, $algos) ) {
            $hashAlgo = $preferred;
            break;
        }
    }
    if ( $hashAlgo === null ) { list($hashAlgo) = $algos; }
    $boundary =
        '----------------------------' .
        substr(hash($hashAlgo, 'cURL-php-multiple-value-same-key-support' . microtime()), 0, 12);

    $body = array();
    $crlf = "\r\n";
    $fields = array();
    foreach ( $postfields as $key => $value ) {
        if ( is_array($value) ) {
            foreach ( $value as $v ) {
                $fields[] = array($key, $v);
            }
        } else {
            $fields[] = array($key, $value);
        }
    }
    foreach ( $fields as $field ) {
        list($key, $value) = $field;
        if ( strpos($value, '@') === 0 ) {
            preg_match('/^@(.*?)$/', $value, $matches);
            list($dummy, $filename) = $matches;
            $body[] = '--' . $boundary;
            $body[] = 'Content-Disposition: form-data; name = "' . $key . '"; filename = "' . basename($filename) . '"';
            $body[] = 'Content-Type: application/octet-stream';
            $body[] = '';
            $body[] = file_get_contents($filename);
        } else {
            $body[] = '--' . $boundary;
            $body[] = 'Content-Disposition: form-data; name = "' . $key . '"';
            $body[] = '';
            $body[] = $value;
        }
    }
    $body[] = '--' . $boundary . '--';
    $body[] = '';
    $contentType = 'multipart/form-data; boundary=' . $boundary;
    $content = join($crlf, $body);
    $contentLength = strlen($content);

    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Length: ' . $contentLength,
        'Expect: 100-continue',
        'Content-Type: ' . $contentType,
    ));

    curl_setopt($ch, CURLOPT_POSTFIELDS, $content);

}

И использовать это:

curl_setopt_custom_postfields($ch, array(
    'file' => '@a.csv',
    'name' => array('James', 'Peter', 'Richard'),
));

См. Ответ @ BeauSimensen ниже.

Jehong Ahn 27.09.2019 05:59
  1. Проголосуйте за Ошибка PHP № 51634.
  2. Попробуйте ответить @ BeauSimensen.
  3. Жрать может это сделать. См. Пример ниже.
$client = new \GuzzleHttp\Client();
$client->request('POST', $url, [
  'multipart' => [
    [ 'name' => 'foo', 'contents' => 'bar' ],
    [ 'name' => 'foo', 'contents' => 'baz' ],
  ]
]);

НЕ ИСПОЛЬЗУЙТЕ GUZZLE:

# at your command line start php interactive
user@group:~:php -a
php > $arr=array('var' => array(1,2,3,4)); 
php > echo http_build_query($arr);
var%5B0%5D=1&var%5B1%5D=2&var%5B2%5D=3&var%5B3%5D=4
php > echo urldecode(http_build_query($arr));
var[0]=1&var[1]=2&var[2]=3&var[3]=4

Итак, вам нужен http_build_query, где вы передаете хеш-массив ключей и значений; ваша (массив) переменная вводится как ключ со значением массива вместо скалярного значения, такого как 'var' => array(1,2,3,4). Теперь http_build_query может форматировать поля сообщения команды curl:

$fields = array('key1' => 'value1', 'var' => array(1,2,3,4));
$curlPost = \http_build_query($fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);

это 3 строки кода! сколько тысяч строк кода в Guzzle? (*)

До сих пор я использовал curl для:

это замена миллиона строк какими-то сотнями!

(*): результат http_build_query может быть отформатирован в соответствии с вашими потребностями.

Стоит отметить, что для http_build_query по умолчанию используется http_build_query($fields, null, ini_get('arg_separator.output'), PHP_QUERY_RFC1738).

centurian 25.07.2020 20:22

Другие вопросы по теме