Guzzle 6: загружать файлы с данными массива

Мне нужно отправить данные массива с файлами. Использование только данных работает правильно:

$client->post('http://xxx/', [
    'form_params' => [
        [
            'data' => ['id' => 1234, 'name' => 'nombre'],
            'valid' => true
        ]
    ]
]);

Но поскольку я не могу использовать «form_params» с «multipart», как я могу отправлять файлы с массивами и логическими данными?

Я пробовал:

$client->post('http://xxx/', [
    'multipart' => [
        [
            'name'     => 'myfile',
            'contents' => fopen('my_file.txt', 'r'),
        ],
        [
            'name'     => 'data',
            'contents' => ['id' => 1234, 'name' => 'nombre'],
        ]
        [
            'name'     => 'valid',
            'contents' => true,
        ]
    ],
]);

но я получаю сообщение об ошибке, так как «содержимое» не принимает логические значения или значения массива.

Мне нужна помощь.

Спасибо

Обновление: я не смог решить проблему, и, наконец, мне пришлось использовать не очень хорошее решение, включая в качестве строки запроса параметр формы и используя только Multipart. Что-то вроде:

$client->post('http://xxx?id=1234&name=nombre', [
'multipart' => [
    [
        'name'     => 'myfile',
        'contents' => fopen('my_file.txt', 'r'),
    ],
],

]);


person Averias    schedule 30.12.2015    source источник
comment
Сам борюсь с этой проблемой. Я получил данные массива, охваченные установкой [headers] =› [ [[Content-Type] =› application/x-www-form-urlencoded]. Но я не могу передать файл с этими данными. См. эту проблему о массивах как значениях содержимого github.com/guzzle/guzzle/issues/1177   -  person Brady    schedule 29.01.2016


Ответы (3)


Согласно документации Guzzle:

Значение multipart представляет собой массив ассоциативных массивов, каждый из которых содержит следующие пары ключ-значение:

  • name: (строка, обязательная) имя поля формы
  • содержимое: (StreamInterface/resource/string, обязательно) Данные для использования в элементе формы.
  • заголовки: (массив) Необязательный ассоциативный массив пользовательских заголовков для использования с элементом формы.
  • имя файла: (строка) Необязательная строка для отправки в качестве имени файла в части.

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

$client->post('http://xxx/', [
    'multipart' => [
        [
            'name'     => 'myfile',
            'contents' => fopen('my_file.txt', 'r'),
        ],
        [
            'name'     => 'id',
            'contents' => '1234',
        ],
        [
            'name'     => 'name',
            'contents' => 'nombre',
        ],
        [
            'name'     => 'valid',
            'contents' => 'true',
        ]
    ],
]);

Не зная, как анализируются данные, я не могу точно сказать, что это будет жизнеспособный вариант, но поскольку он принимает настраиваемые заголовки, вы можете отправить его в виде массива JSON (в строковой форме), а затем установить тип содержимого:

$client->post('http://xxx/', [
    'multipart' => [
        [
            'name'     => 'myfile',
            'contents' => fopen('my_file.txt', 'r'),
        ],
        [
            'name'     => 'data',
            'contents' => '{"id":1234, "name":"nombre"}',
            'headers'  => ['Content-Type' => 'text/x-json']
        ],
        [
            'name'     => 'valid',
            'contents' => 'true',
        ]
    ],
]);

Другой вариант — использовать cURL напрямую, хотя я уверен, что вы хотели избежать этого, поскольку используете Guzzle.

ОБНОВЛЕНИЕ

Это не то, что я проверял лично, но вот еще одна вещь, которую вы можете попробовать... (на самом деле я должен был подумать об этом в первую очередь).

$client->post('http://xxx/', [
    'multipart' => [
        [
            'name'     => 'myfile',
            'contents' => fopen('my_file.txt', 'r'),
        ],
        [
            'name'     => 'data',
            'contents' => 'id=1234&name=nombre',
            'headers'  => ['Content-Type' => 'text/plain; charset=ISO-8859-1']
        ],
        [
            'name'     => 'valid',
            'contents' => true,
        ]
    ],
]);
person Jabari    schedule 30.12.2015
comment
Большое спасибо @Jabari, ваш первый вариант невозможен, так как мне нужно отправить массив с ключом данных, второй я уже пробовал, а третий не работает. Единственная возможность - отправить параметр form_param... Есть ли способ отправить файлы внутри параметра form_params? Спасибо еще раз - person Averias; 31.12.2015
comment
@Averias Сегодня я проведу несколько тестов, я тоже заинтересован в том, чтобы это заработало. - person Jabari; 31.12.2015

Есть возможность отправлять загружаемые файлы и данные массива вместе через Guzzle, но в измененном виде:

$client->post('http://xxx/', [
    'multipart' => [
        [
            'name'     => 'myfile',
            'contents' => fopen('my_file.txt', 'r'),
        ],
        [
            'name'     => 'data[0][id]',
            'contents' => 1234,
        ],
        [
            'name'     => 'data[0][name]',
            'contents' => 'nombre',
        ],
        [
            'name'     => 'data[1][id]',
            'contents' => 2222,
        ],
        [
            'name'     => 'data[1][name]',
            'contents' => 'nombre2',
        ],
        [
            'name'     => 'valid',
            'contents' => true,
        ]
    ],
]);

С другой стороны, вы можете получить данные с помощью $_POST['data'], а затем выполнить foreach:

foreach ($_POST['data'] as $data) {
    echo "Id: ".$data['id'];
    echo "Name: ".$data['name'];
}
person fsasvari    schedule 23.11.2016

У вас может быть рекурсивный метод, который получает ваш массив данных и подготавливает составной массив:

private function prepareMultipartParams($params, $baseKey = null): array
{
    $result = [RequestOptions::MULTIPART => []];
    foreach ($params as $key => $value) {
        $key = $baseKey == null ? $key : "{$baseKey}[{$key}]";
        if (is_array($value))
            foreach ($this->prepareMultipartParams($value, $key) as $array)
                $result[RequestOptions::MULTIPART] = array_merge($result[RequestOptions::MULTIPART], $array);
        else
            $result[RequestOptions::MULTIPART][] = [
                'name' => $key,
                'contents' => $value instanceof UploadedFile ? fopen($value->path(), 'r+') : $value
            ];
    }

    return $result;
}
person Thrashzone13    schedule 21.12.2019
comment
круто, спасибо! - person xavi; 19.04.2021