【问题标题】:PHP cURL file get contents is always emptyPHP cURL 文件获取内容始终为空
【发布时间】:2023-10-03 17:49:01
【问题描述】:

我在 Paw(和/或 Postman)中创建了一个端点来处理文件上传。效果很好!

这很简单。在我的应用程序中,我可以执行以下操作:

echo file_get_contents('php://input');

它会打印出图像/文件的编码表示(一只可爱的小猫)。

问题是,我似乎无法仅使用 cURL 重现此行为(当前应用程序使用 Guzzle)。

当我尝试类似:

$target_url = 'http://my-document-handling-service.net:8000/documents';

$request = curl_init($target_url);

curl_setopt($request, CURLOPT_POST, true);
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
curl_setopt($request, CURLOPT_POSTREDIR, 3);

curl_setopt(
    $request,
    CURLOPT_POSTFIELDS,
    array(
      'file' => '/Users/xxxxxxx-xxxx/Documents/kitten-test-upload-image-1.jpg'
    ));

echo curl_exec($request);

curl_close($request);

正文 (file_get_contents()) 始终为空。我不太了解 cURL 我一直只使用 Guzzle - 但现在我必须使用 cURL 并且无法获取正文。

我做错了什么?

【问题讨论】:

  • 也许可以尝试将其更改为 echo file_get_contents($_POST['file']); .... 尽管您可能应该在将其放入 file_get_contents 之前进行一些过滤和白名单并检查该值...只是说。那是可怕的安全风险。
  • 然后我得到Warning: file_get_contents(/Users/mike/code/local-dev/kitten.jpg): failed to open stream: No such file or directory in ... (my docker filepath)。所以它就像它不发送文件 - 它只是发送文件的名称?
  • 对,您正在向文件发送“路径”。所以我假设my-document-handling-service.net 上不存在该路径。这反过来又抛出了这个错误。所以你正试图通过 curl 发送 ACTUAL 文件?
  • 在路径前添加'@' ....'file' => '@'.'/Users/xxxxxxx-xxxx/Documents/kitten-test-upload-image-1.jpg'

标签: php curl guzzle


【解决方案1】:

(评论太长,现有答案非常接近正确,尽管存在错误并且无法正常工作,但没有说明您的原始代码失败的原因) - 当您给 CURLOPT_POSTFIELDS 一个数组时,它会编码以multipart/form-data-format 输入,PHP 将 multipart/form-data 解析为 $_POST(对于文件上传,$_FILES),并在此过程中清空 php://input(它对它内置的任何编码执行此操作-支持并在撰写本文时,有 2 个,application/x-www-form-urlencoded, andmultipart/form-data`),以及您的代码

curl_setopt(
    $request,
    CURLOPT_POSTFIELDS,
    array(
      'file' => '/Users/xxxxxxx-xxxx/Documents/kitten-test-upload-image-1.jpg'
    ));

将变量file的值'/Users/xxxxxxx-xxxx/Documents/kitten-test-upload-image-1.jpg'multipart/form-data格式发送到服务器,因此你的值实际上是$_POST['file'],但是如果你想直接发送原始图像,没有编码,所以您可以在目标服务器上使用 php://input,例如使用 CURLOPT_INFILE 或 CURLOPT_POSTFIELDS

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_INFILE,($fp=fopen('abc.txt','rb')));
curl_setopt($ch, CURLOPT_INFILESIZE,123);
(...)
curl_exec($ch);
fclose($fp);

curl_setopt($ch,CURLOPT_POSTFIELDS,file_get_contents("abc.txt"));
curl_setopt($ch,CURLOPT_HTTPHEADER,array('Content-Type: application/octet-stream'));
curl_exec($ch);

(注意第二种方法会占用更多内存,上传前会将整个文件放入内存中,而第一种方法会分块发送数据,这样就可以上传任意大小的文件而不会耗尽内存,不像第二种方法)

【讨论】:

  • 第一个不起作用,但第二个对 file_get_contents('php://input') 起作用。虽然有相当大的延迟,所以我认为它不适合这个。也许我的整个方法都是错误的,我试图实现这里为我的 api 提出的想法:philsturgeon.uk/api/2016/01/04/http-rest-api-file-uploads
  • @mikelovelyuk 我的建议是,目前,只支持标准的 multipart/form-data 格式,它将为您在执行任何操作时省去很多麻烦。也许在将来需要对代码进行微优化时,您可以重新访问原始上传方法(因为它确实具有较低的开销,但更难以有效支持,当您发送大文件时,nginx、apache 和 php 都不喜欢它以这种方式文件,但 multipart/form-data 几乎在所有地方都得到了很好的支持。)
【解决方案2】:

通常 CURL 将发送使用 application/x-www-form-urlencoded 或 multipart/form-data 编码的 Postfields。 (Details)。

您需要的是 POST 数据中的 RAW 编码。我还没有测试它。但它会是这样的:

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
$res=fopen("abc.txt","rb"));   //CURLOPT_INFILE needs a stream resource
curl_setopt($ch, CURLOPT_INFILE, $res);
curl_setopt($ch, CURLOPT_INFILESIZE,123);
...
curl_exec($ch);
...
fclose($res);

【讨论】:

  • 如果这行得通,我会很感兴趣。我也无法对其进行测试……但听起来很有希望。
  • 如果他的代码没有被窃听,这将起作用,INFILE 需要 FILE 资源,而不是字符串。 curl_setopt($ch, CURLOPT_INFILE,fopen("abc.txt","rb")); 应该可以工作,但会泄漏内存。 curl_setopt($ch, CURLOPT_INFILE,($fp=fopen("abc.txt","rb"))); curl_exec($ch); fclose($fp); 也应该可以工作,并且不会泄漏任何内存。
  • 这是正确的hanshenrik。谢谢你的提示。我更正了答案。
  • 我试过这个(编辑前后)。仍然 file_get_contents、$_FILES、$_POST 等都是 NULL 或只是一个空字符串。