Я использую этот 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 Произошла ошибка. Пожалуйста, попробуйте еще раз, и если проблема не исчезнет, свяжитесь с нами. "
Если вы хотите загрузить реально существующий файл, используйте класс CURLFile. Если вы хотите «подделать» загрузку файла, используя данные, которые вы получили в виде строки, используйте класс CURLStringFile.
Я изменился на $xml = '@file.xml'; и Content-Type: multipart/form-data - но все равно ошибка 500. Сейчас попробую другой. Спасибо
Теперь я изменил $xml['file'] = new CurlFile('file.xml', 'multipart/form-data', 'file.xml'); Но выдает предупреждение: преобразование массива в строку в curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml"); Что мне делать дальше?
xmlRequest=$xml по-прежнему совершенно не подходит для составного запроса. Вы должны предоставить массив CURLOPT_POSTFIELDS и позволить cURL обрабатывать все остальное (включая настройку заголовка Content-Type, поскольку он должен включать используемое граничное значение).






если ваш 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--
curl_setopt($ch, CURLOPT_HTTPHEADER-вызовом, был внешним Content-Type (неправильным)(Некоторые варианты этого вопроса задавались много раз раньше, но я не могу найти дубликат прямо сейчас, странно)
Я также разместил ответ и в основном пришел к тем же выводам. Кроме того, решение на основе tempfile(), которое у вас есть в вашем вопросе, кажется мне законным. Как вы спросили «еще?», начиная с PHP 8.1 есть: CURLStringFile. И у меня также были проблемы с поиском соответствующего дубликата, поскольку этот вопрос в основном спрашивает, как преобразовать командную строку curl в curl PHP, у которого может быть много ответов. Что касается того, как файл загрузить строку, я нашел этот вопрос и ответ проницательным: stackoverflow.com/a/72323650/367456
Это работает. Спасибо :)
Как правило, вы можете сказать, что вы ищете каждый из параметров командной строки curl (1) (+ аргументы) и операндов и находите соответствующий curl_setopt ($ option, $ value).
Таким образом, вы можете сравнить командную строку с кодом PHP, чтобы найти потенциальные проблемы, которые могут вызвать ошибку 500 (ссылка). В противном случае вы не сможете выяснить, что не так в PHP-коде, пока работает командная строка.
В следующем ответе я покажу, как это делается шаг за шагом. И у него есть примеры PHP-кода, как загрузить файл как из файловой системы, так и из строки с содержимым файла.
Итак, давайте посмотрим, что у нас есть в этой командной строке:
curl.exe https://example.com/rest -u "xx:yy" -F [email protected] > result.csv
https://example.com/rest (URL)
Синтаксис URL зависит от протокола. Подробное описание вы найдете в RFC 3986. (ссылка)
-u "xx:yy" (<user:password>)
Укажите имя пользователя и пароль, которые будут использоваться для аутентификации сервера. (ссылка)
-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);
curl_setopt($ch, CURLOPT_USERPWD, 'xx:yy'); - ОК, соответствует 2.) выше.curl_setopt($ch, CURLOPT_URL,$url); - ОК, соответствует 1.) выше.curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - Хорошо, ваш конфиг. (Как вы написали, вы хотите сделать это позже.)curl_setopt($ch, CURLOPT_TIMEOUT, 10); - Хорошо, ваш конфиг. (Также хорошо для более быстрого тестирования.)curl_setopt($ch, CURLOPT_POST, true); - ОК, отражает метод POST, соответствующий 3.) выше.curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml"); - НЕ ОК, не соответствует 3.) выше. 3.) выше выполняет загрузку файла, здесь вы устанавливаете значение строки формы. Это никогда не загрузка файла. Интересно, давайте посмотрим на это.Я могу представить, что это может вызвать 500 на удаленной стороне, поскольку у них может быть небольшая обработка ошибок, и процесс просто падает, следовательно, выдает 500 Internal Server Error и не предоставляет информацию о том, что в деталях не так с самим запросом. (диапазон кодов состояния HTTP 400–499).
Давайте посмотрим, как превратить это в загрузку файла. Есть два варианта: из строки или из пути.
Возможно, вы хотите создать загрузку файла из строки, чтобы данные загрузки файла были содержимым строки.
Если это так, вот как это работает в 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, адаптированном к вашему -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.
Продолжим со следующими строками:
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - НЕ ОК, в оригинальной командной строке curl этого нет. Убери это.
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".
Привет еще раз, теперь я борюсь с этим > result.csv. Данные представляют собой очень большую строку. И когда я конвертирую в массив, просто становлюсь 1 большим значением со всем в нем. Можете ли вы помочь, пожалуйста?
@Pekzz: Для вашего первого комментария: Спасибо, что подняли этот вопрос, это CURLFile, друг CURLStringFile в ответе. Поскольку оба являются близкими друзьями, и их нельзя отделять друг от друга, я отредактировал ответ, чтобы показать эту альтернативу. Второй комментарий мне не так ясен, почему бы сначала не сохранить его как файл, а затем работать с этим файлом? Это помогает вам отделять проблемы друг от друга и упрощает получение помощи, поскольку вы просто озабочены их разбором и обработкой в PHP, и это также зависит от формата (у вас есть CSV, верно?)
Ух ты..!! CURLFile('file.xml') это работает очень хорошо и намного короче коды. Большое спасибо, что вернулись и все объяснили :) Я не программист, просто энтузиаст. Я удалил коды в основном с этого веб-сайта, чтобы учиться и создавать простые приложения для развлечения или для облегчения своей работы. Благодаря таким людям, как вы, которые стараются объяснить и устранить неполадки, я могу многому научиться. Что касается моего второго вопроса, я решил его [ссылка]stackoverflow.com/questions/76672715/… Спасибо.
Синтаксис
@file.xmlзаставляет cURL выполнять фактическую загрузку файла, что отличается от простого предоставления обычного текстового параметра, содержащего XML в строковой форме. А при загрузке файла исходная команда тоже отправляла неContent-Type: text/xml, аContent-Type: multipart/form-data.