如何在不将文件加载到内存的情况下使用 Guzzle 发送文件

How to send files with Guzzle without loading them into memory

我有一个表单,可以将多个文件上传到我的 laravel 后端,我想用 Guzzle 将所有这些文件发送到外部 API

我遇到一个问题,如果我上传的 MB 超过可用内存,我的脚本 运行 内存不足。错误信息是

Allowed memory size of ... bytes exhausted (tried to allocate ... bytes)

不幸的是,我无法动态更改内存限制

这是我使用的代码

// in laravel controller method

/* @var \Illuminate\Http\Request $request */
$files = $request->allFiles();

$filesPayload = [];

foreach ($files as $key => $file) {
    $filesPayload[] = [
        'name'     => $key,
        'contents' => file_get_contents($file->path()),
        // 'contents' => fopen($file->path(), 'r'), // memory issue as well
        'filename' => $file->getClientOriginalName(),
    ];
}

$client = new \GuzzleHttp\Client\Client([
    'base_uri' => '...',
]);

$response = $client->post('...', [
    'headers' => [
        'Accept'         => 'application/json',
        'Content-Length' => ''
    ],
    'multipart' =>  $filesPayload,
]);

我正在使用 Guzzle 6。In the docs我找到了 fopen 的示例,但这也引发了内存错误

有没有一种方法可以使用 Guzzle 发送多个文件而不将它们加载到内存中?

为了解决这个问题,我添加了一个 curl 选项,您可以在请求上指定该选项,它将请求正文作为字符串发送,而不是从请求的实体正文中流式传输。您可以像这样启用此行为:

$options = $client->getConfig()->get('curl.options');
$options['body_as_string'] = TRUE;
$client->getConfig()->set('curl.options', $options);

我终于设法通过更改

完成了这项工作
'contents' => file_get_contents($file->path()),

'contents' => \GuzzleHttp\Psr7\stream_for(fopen($file->path(), 'r'))

通过此更改,文件未加载到内存中,我能够发送更大的文件

我也在努力使用 Guzzle 发送大文件(2 GB 到 5 GB),最后我使用了 curl php,它的效果非常好:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, '/url');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$headers = [];
$headers[] = 'Content-Type: multipart/form-data';
$headers[] = 'Cookie: something...';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
$path = '/full/path/to/file';
$file = curl_file_create(realpath($path));
$post = [   
    'file' => $file,
    'other_field' => 'value',
];
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
var_dump($result,$httpcode);