Curl -F [email protected] в PHP

Я использую этот cmd для успешной загрузки result.csv

curl.exe https://example.com/rest -u "xx:yy" -F [email protected] > result.csv

Я хочу сделать это с помощью PHP. Но без толку... Помоги пожалуйста.

Это мой PHP:

$url = 'https://example.com/rest';
$xml = '<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?>
<request xmlns = "http://example.com"><search path = "report_date" value = "2023-07-01T00:00:00+0000"/>'; //and so on..
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERPWD, 'xx:yy');
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

$data = curl_exec($ch);
var_dump($data); //just to check if it has requested data (will save to file later)
if (curl_errno($ch))
    print curl_error($ch);
else
    curl_close($ch);

У него нет запрошенных данных. Но это дает это

строка(346) " 500 Произошла ошибка. Пожалуйста, попробуйте еще раз, и если проблема не исчезнет, ​​свяжитесь с нами. "

Синтаксис @file.xml заставляет cURL выполнять фактическую загрузку файла, что отличается от простого предоставления обычного текстового параметра, содержащего XML в строковой форме. А при загрузке файла исходная команда тоже отправляла не Content-Type: text/xml, а Content-Type: multipart/form-data.

CBroe 12.07.2023 08:09

Если вы хотите загрузить реально существующий файл, используйте класс CURLFile. Если вы хотите «подделать» загрузку файла, используя данные, которые вы получили в виде строки, используйте класс CURLStringFile.

CBroe 12.07.2023 08:12

Я изменился на $xml = '@file.xml'; и Content-Type: multipart/form-data - но все равно ошибка 500. Сейчас попробую другой. Спасибо

Pekzz 12.07.2023 08:37

Теперь я изменил $xml['file'] = new CurlFile('file.xml', 'multipart/form-data', 'file.xml'); Но выдает предупреждение: преобразование массива в строку в curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml"); Что мне делать дальше?

Pekzz 12.07.2023 09:12
xmlRequest=$xml по-прежнему совершенно не подходит для составного запроса. Вы должны предоставить массив CURLOPT_POSTFIELDS и позволить cURL обрабатывать все остальное (включая настройку заголовка Content-Type, поскольку он должен включать используемое граничное значение).
CBroe 12.07.2023 09:17
Стоит ли изучать 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 и хотите разрабатывать...
1
5
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

если ваш php-код имеет его в виде строки, а не файла, то вам сначала нужно поместить его в файл (поскольку libcurl API PHP не поддерживает (пока?) загрузку файлов в multipart/form-data из строк), чтобы обойти это ограничение, вы можно сделать что-то вроде

function stringToFile(string $data): array
{
    $ret = array(
        "handle" => tmpfile(),
        "path" => null,
    );
    $ret['path'] = stream_get_meta_data($ret['handle'])['uri'];
    fwrite($ret['handle'], $data);
    rewind($ret['handle']);
    return $ret;
}

затем измените свой код на

$url = 'https://example.com/rest';
$xml = '<?xml version = "1.0" encoding = "UTF-8" standalone = "yes"?>
<request xmlns = "http://example.com"><search path = "report_date" value = "2023-07-01T00:00:00+0000"/>'; //and so on..
$xmlAsFile = stringToFile($xml);
$ch = curl_init();

curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
    'xmlRequest' => new CURLFile($xmlAsFile['path'], 'application/xml', 'file.xml')
));
(...)

также вам нужно удалить это:

curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));

в multipart/form-data-запросах Content-Type должен быть чем-то вроде Content-Type: multipart/form-data; boundary=------------------------e74d6203325745a2

но libcurl генерирует этот заголовок автоматически, и вы рискуете испортить запрос POST, сделав его недействительным и неразборчивым, если вы сами измените внешний Content-Type.

Исправьте эти две вещи, и ваш код должен работать.

Между тем, ваш файл.xml будет иметь свой собственный "внутренний" Content-Type, вот полный пример как с внешним, так и с внутренним Content-Type:

POST / HTTP/1.1
Host: localhost:9999
User-Agent: curl/8.1.2
Accept: */*
Content-Length: 203
Content-Type: multipart/form-data; boundary=------------------------0e405b89d0ec7f23

--------------------------0e405b89d0ec7f23
Content-Disposition: form-data; name = "file"; filename = "test.xml"
Content-Type: application/xml

<xml></xml>

--------------------------0e405b89d0ec7f23--
  • как видите, есть как внешний, так и внутренний Content-Type, и тот, который вы перезаписали своим curl_setopt($ch, CURLOPT_HTTPHEADER-вызовом, был внешним Content-Type (неправильным)

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

Я также разместил ответ и в основном пришел к тем же выводам. Кроме того, решение на основе tempfile(), которое у вас есть в вашем вопросе, кажется мне законным. Как вы спросили «еще?», начиная с PHP 8.1 есть: CURLStringFile. И у меня также были проблемы с поиском соответствующего дубликата, поскольку этот вопрос в основном спрашивает, как преобразовать командную строку curl в curl PHP, у которого может быть много ответов. Что касается того, как файл загрузить строку, я нашел этот вопрос и ответ проницательным: stackoverflow.com/a/72323650/367456

hakre 12.07.2023 11:22

Это работает. Спасибо :)

Pekzz 12.07.2023 12:07
Ответ принят как подходящий

Как правило, вы можете сказать, что вы ищете каждый из параметров командной строки curl (1) (+ аргументы) и операндов и находите соответствующий curl_setopt ($ option, $ value).

Таким образом, вы можете сравнить командную строку с кодом PHP, чтобы найти потенциальные проблемы, которые могут вызвать ошибку 500 (ссылка). В противном случае вы не сможете выяснить, что не так в PHP-коде, пока работает командная строка.

В следующем ответе я покажу, как это делается шаг за шагом. И у него есть примеры PHP-кода, как загрузить файл как из файловой системы, так и из строки с содержимым файла.

Итак, давайте посмотрим, что у нас есть в этой командной строке:

curl.exe https://example.com/rest -u "xx:yy" -F [email protected] > result.csv
  1. https://example.com/rest (URL)

    Синтаксис URL зависит от протокола. Подробное описание вы найдете в RFC 3986. (ссылка)

  2. -u "xx:yy" (<user:password>)

    Укажите имя пользователя и пароль, которые будут использоваться для аутентификации сервера. (ссылка)

  3. -F [email protected] (<name=content>)

    Для семейства протоколов HTTP это позволяет curl эмулировать заполненную форму, в которой пользователь нажал кнопку отправки. Это приводит к закручиванию данных POST с использованием multi-part/form-data Content-Type в соответствии с RFC 2388.

    Это позволяет загружать двоичные файлы и т. д. Чтобы заставить часть «содержимое» быть файлом, поставьте перед именем файла знак @. Чтобы просто получить часть содержимого из файла, добавьте к имени файла префикс символа <. Разница между @ и < заключается в том, что @ заставляет файл прикрепляться к сообщению как загрузку файла, а < создает текстовое поле и просто получает содержимое для этого текстового поля из файла (ссылка)

Затем посмотрим, какие операции curl_setopt() есть в PHP-коде:

$ch = curl_init();
curl_setopt($ch, CURLOPT_USERPWD, 'xx:yy');
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml'));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  1. curl_setopt($ch, CURLOPT_USERPWD, 'xx:yy'); - ОК, соответствует 2.) выше.
  2. curl_setopt($ch, CURLOPT_URL,$url); - ОК, соответствует 1.) выше.
  3. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - Хорошо, ваш конфиг. (Как вы написали, вы хотите сделать это позже.)
  4. curl_setopt($ch, CURLOPT_TIMEOUT, 10); - Хорошо, ваш конфиг. (Также хорошо для более быстрого тестирования.)
  5. curl_setopt($ch, CURLOPT_POST, true); - ОК, отражает метод POST, соответствующий 3.) выше.
  6. curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml"); - НЕ ОК, не соответствует 3.) выше. 3.) выше выполняет загрузку файла, здесь вы устанавливаете значение строки формы. Это никогда не загрузка файла. Интересно, давайте посмотрим на это.

Я могу представить, что это может вызвать 500 на удаленной стороне, поскольку у них может быть небольшая обработка ошибок, и процесс просто падает, следовательно, выдает 500 Internal Server Error и не предоставляет информацию о том, что в деталях не так с самим запросом. (диапазон кодов состояния HTTP 400–499).

Давайте посмотрим, как превратить это в загрузку файла. Есть два варианта: из строки или из пути.

Загрузка файла PHP CURL из строки

Возможно, вы хотите создать загрузку файла из строки, чтобы данные загрузки файла были содержимым строки.

Если это так, вот как это работает в PHP Curl, адаптированном к вашей опции -F [email protected] curl (1) и примеру аргумента:

curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'xmlRequest' => new CURLStringFile($xml, 'file.xml'),
    #     ^                              ^       ^
    #     |                              |       `-- upload filename
    # name of the form-field             |
    #                                    |
    #                   string contents of the file (plain binary data)
]);

См. CURLStringFile; ответ на Отправить строку в виде файла с помощью curl и php и curl_setopt(CURLOPT_POSTFIELDS) (в нем уже упоминается CURLStringFile); CURLStringFile доступен, начиная с PHP 8.1.

Загрузка файла PHP CURL из пути

Если это не так, и вы хотите загрузить файл из файловой системы (вместо строковых данных), вот как это работает в PHP Curl, адаптированном к вашему -F [email protected] curl(1) пример опции и аргумента:

curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'xmlRequest' => new CURLFile('file.xml'),
    #     ^                          ^
    #     |                          `-- upload filename
    # name of the form-field  
]);

См. CURLFile; CURLFile доступен начиная с PHP 5.5.

Продолжим со следующими строками:

  1. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.

  2. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.

  3. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.

  4. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.

Как вы уже писали, вы еще не заинтересованы в преобразовании перенаправления в файл (> result.csv в конце командной строки), так что это уже должно быть для ошибки 500, с которой вы столкнулись.

Обсуждение

Техника сравнения командной строки Curl с кодом PHP Curl — хорошая процедура. поскольку это обеспечивает прямой способ устранения неполадок и всегда может быть завершено.

Это не должно означать, что это решит все проблемы; однако, если завершение и ошибка не устранена, ее легче локализовать и выявить различия в конфигурации между curl в командной строке и расширением PHP Curl. Для таких проблем часто необходимо просмотреть все вызовы curl_setopt() (и их порядок), что уже является частью процедуры и, следовательно, позволяет вам обрабатывать проблемы более высокого уровня.

Приведенный ответ может также показать, как перенести (перевести) командную строку Curl в PHP Curl, однако он не показывает, как найти правильные параметры PHP Curl (curl_setopt() содержит список всех параметров) и их соответствующий порядок (установка одних параметров влияет или сбрасывает другие) в деталях. Вы также можете найти на сайте довольно много вопросов и ответов по теме в целом, как для Curl в командной строке, так и для PHP Curl.

Например, PHP — отладка Curl показывает, как получить подробную информацию и информацию о подключении для PHP Curl.

Кроме того, существуют инструменты для создания шаблонного PHP-кода из командной строки curl, например Curl-to-PHP, но будьте осторожны, такие инструменты могут быть ограничены (это с 2017 года, в PHP эта дата — это год выпуска PHP 7.2 от 30 ноября, и с этим расширением PHP Curl требуется libcurl версии 7.10.5, которая довольно устарела).

Дополнительно: если у вас есть Composer в вашем наборе инструментов, вы можете запустить диагностика композитора , которая показывает имеющуюся у вас версию PHP в командной строке, а также версию Curl расширения PHP Curl ( ext-curl — это название пакета платформы в Composer):

> composer diagnose
...
PHP version: 8.2.8
...
cURL version: 7.81.0 libz 1.2.11 ssl OpenSSL/3.0.2
...

(Это похоже на php_version() и curl_version(), которые вы можете использовать с самим PHP.)

Привет, я изменил $xml = 'file.xml'; и curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'xmlRequest' => new CURLStringFile($xml, 'file.xml'),]); Я думаю, что это прошло, но неправильно прочитал содержимое файла.xml. Так что это дает мне неправильный результат: строка (29) "REPORT_FAILED null".

Pekzz 12.07.2023 12:14

Привет еще раз, теперь я борюсь с этим > result.csv. Данные представляют собой очень большую строку. И когда я конвертирую в массив, просто становлюсь 1 большим значением со всем в нем. Можете ли вы помочь, пожалуйста?

Pekzz 12.07.2023 15:30

@Pekzz: Для вашего первого комментария: Спасибо, что подняли этот вопрос, это CURLFile, друг CURLStringFile в ответе. Поскольку оба являются близкими друзьями, и их нельзя отделять друг от друга, я отредактировал ответ, чтобы показать эту альтернативу. Второй комментарий мне не так ясен, почему бы сначала не сохранить его как файл, а затем работать с этим файлом? Это помогает вам отделять проблемы друг от друга и упрощает получение помощи, поскольку вы просто озабочены их разбором и обработкой в ​​PHP, и это также зависит от формата (у вас есть CSV, верно?)

hakre 13.07.2023 10:07

Ух ты..!! CURLFile('file.xml') это работает очень хорошо и намного короче коды. Большое спасибо, что вернулись и все объяснили :) Я не программист, просто энтузиаст. Я удалил коды в основном с этого веб-сайта, чтобы учиться и создавать простые приложения для развлечения или для облегчения своей работы. Благодаря таким людям, как вы, которые стараются объяснить и устранить неполадки, я могу многому научиться. Что касается моего второго вопроса, я решил его [ссылка]stackoverflow.com/questions/76672715/… Спасибо.

Pekzz 13.07.2023 13:03

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