curl [email protected][email protected]

83qze16e  于 4个月前  发布在  其他
关注(0)|答案(2)|浏览(59)

我使用此命令成功下载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);


它没有请求的数据。但它给出了
string(346)“500发生错误。请重试,如果问题仍然存在,请与我们联系。“

2cmtqfgy

2cmtqfgy1#

如果您php代码将其作为字符串,而不是文件,那么您首先必须将其放入文件中(因为PHP的libcurl API(尚未?)支持从字符串上传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-requests中,Content-Type应该类似于Content-Type: multipart/form-data; boundary=------------------------e74d6203325745a2
但是libcurl会自动为你生成这个头,如果你自己改变了外部的Content-Type,你就有可能破坏POST请求,使之无效和不可解析。

**修复这两个东西,你的代码应该工作。

同时,你的file.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-call调用的那个是外部Content-Type(错误的那个)

(Some这个问题的变体以前被问过很多次,但我现在找不到重复的,奇怪)

vxqlmq5t

vxqlmq5t2#

根据经验,您可以查找curl(1)命令行选项(+参数)和操作数中的每一个,并找到适当的curl_setopt($option,$value)。
因此,您可以将命令行与PHP代码进行比较,以查找可能导致500错误(ref)的潜在问题。否则,您无法在命令行工作时找出PHP代码中的错误。
在下面的答案中,我将逐步展示如何做到这一点。它有PHP代码示例,说明如何从文件系统上传文件,以及如何从包含文件内容的字符串上传文件。
让我们来看看我们在命令行中得到了什么:

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

字符串

  1. https://example.com/restURL
    URL的语法是依赖于协议的。你可以在RFC 3986中找到详细的描述。(ref
  2. -u "xx:yy"<user:password>
    指定用于服务器身份验证的用户名和密码。(ref
  3. -F [[email protected]](https://stackoverflow.com/cdn-cgi/l/email-protection)<name=content>
    对于HTTP协议族,这让curl模拟一个用户按下提交按钮的已填充表单。这会导致curl根据RFC 2388使用Content-Type multi-part/form-data来POST数据。
    这使得上传二进制文件等。要强制“内容”部分是一个文件,前缀的文件名与@符号。只是从一个文件的内容部分,前缀的文件名与符号<。@和<之间的区别是,@使一个文件得到附加在后作为一个文件上传,而<则创建一个文本字段,并从文件(ref)中获取该文本字段的内容
    然后让我们看看PHP代码中有哪些curl_setopt()操作:
$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'); - OK,matches 2.)above.
  2. curl_setopt($ch, CURLOPT_URL,$url);- OK,匹配上面的1.)。
  3. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);-好的,你的配置。(正如你所写的,你想以后再做这个。)
  4. curl_setopt($ch, CURLOPT_TIMEOUT, 10);-确定,您的配置。(也有利于更快的测试。)
  5. curl_setopt($ch, CURLOPT_POST, true);- OK,镜像与上述3.)匹配的POST方法。
  6. curl_setopt($ch, CURLOPT_POSTFIELDS, "xmlRequest=$xml"); - NOT OK,does not match 3.)above. 3.)above does a file-upload,here you're setting a form string value. This is never a file-upload. Interesting,let's look into that.
    我可以想象这可能会在远程端触发500,因为它们可能没有什么错误处理,进程只是崩溃,因此给出了一个 *500内部服务器错误 *,而没有提供请求本身的详细错误信息(400-499 HTTP状态代码范围)。
    让我们看看如何将其转换为文件上传。有两种变体:From string或from pathname。

PHP CURL文件上传

您可能希望从字符串创建一个文件上传,以便文件上传的数据是字符串内容。
如果是这样的话,这就是它在PHP Curl中的工作方式,为您的-F [[email protected]](https://stackoverflow.com/cdn-cgi/l/email-protection) 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; answer to Send string as a file using curl and phpcurl_setopt(CURLOPT_POSTFIELDS)(它已经提到了 CURLStringFile);CURLStringFile自PHP 8.1起可用。

从路径名上传PHP CURL文件

如果不是这样的话,你想从文件系统上传文件(而不是字符串数据),这是PHP Curl中的工作方式,它是根据你的-F [[email protected]](https://stackoverflow.com/cdn-cgi/l/email-protection) 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命令行到PHP Curl,但是,它没有显示如何找到正确的PHP Curl选项(curl_setopt()列出了所有选项)及其适当的顺序(设置 * 一些 * 选项影响或重置其他选项)。你也可以在网站上找到关于这个主题的问答,在命令行和PHP Curl中都是如此。
例如,PHP - Debugging Curl展示了如何获取PHP Curl的详细信息和连接信息。

此外,还有一些工具可以从curl命令行生成样板PHP代码,例如Curl-to-PHP,但要注意,这样的工具可能是有限的(这是从2017年开始的,在PHP中,日期是11月30日PHP 7.2发布的年份,PHP Curl扩展需要libcurl版本7.10.5,这是相当过时的)。

**额外:**如果你的工具包里有Composer,你可以运行composer diagnose,它会在命令行上显示你手头上的PHP版本,以及PHP Curl扩展的Curl版本(ext-curlplatform package in Composer的名称):

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

相关问题