S3:putObject() 通过 POST 接收的流

Mar*_* C. 3 php amazon-s3 amazon-web-services

我正在接收文件(最大 4 GB):文件内容通过 POST 请求正文传输给我。我想将此流直接上传到 s3 存储桶,而不先在本地保存。已经尝试了不同的方法,但由于不同的原因而失败了。

我目前的做法:

use GuzzleHttp\Psr7\Stream;
use Aws\S3\S3Client;

$s3 = new \Aws\S3\S3Client([
    'version' => 'latest',
    'region' => 'eu-west-1',
    'credentials' => [
        'key' => 'abc',
        'secret' => '123'
    ]
]);

$stream = new \GuzzleHttp\Psr7\Stream(fopen('php://input', 'r'));

$result = $s3->putObject(array(
    'Bucket' => $bucket,
    'Key' => $keyname,
    'ContentLength' => (int)$_SERVER['CONTENT_LENGTH'],
    'Body' => $stream->getContents(),
    'ACL' => 'private',
    'StorageClass' => 'STANDARD_IA',
));
Run Code Online (Sandbox Code Playgroud)

尝试流式传输 80 MB 文件时出现以下错误:

PHP message: PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 78847383 bytes) in /var/www/slimapi/vendor/slim/slim/Slim/Http/Stream.php on line 403
Run Code Online (Sandbox Code Playgroud)

Stream.php的第403行是:

if (!$this->isReadable() || ($contents = stream_get_contents($this->stream)) === false) {
Run Code Online (Sandbox Code Playgroud)

因此,该错误可能是由于尝试将流的全部内容加载到字符串中而导致的,这超出了内存限制。(令人恼火的是为什么错误发生在 Slim/Stream 中,因为我正在尝试使用 guzzle\Stream。)

所以我的问题是:如何将传入的 POST 数据直接流式传输到 s3 存储桶,而不会出现导致内存问题的缓冲问题?

我已经尝试过:

  • $stream = Psr7\stream_for(fopen('php://input', 'r'));
  • $stream = fopen('php://input', 'r');
  • 在 putObject() 中: 'Body' => Stream::factory(fopen('php://input', 'r')),

Zde*_*něk 6

我知道这是老话题,但它没有标记为已解决,所以......

PHP SDK确实支持流源,正如您在SDK规范中看到的那样(https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#putobject ) - 请参阅参数语法:

$result = $client->putObject([
    // ...
    'Body' => <string || resource || Psr\Http\Message\StreamInterface>,
    // ...
]);
Run Code Online (Sandbox Code Playgroud)

这意味着您的代码几乎没问题,唯一的是您应该通过$stream而不是$stream->getContents()

$stream = new \GuzzleHttp\Psr7\Stream(fopen('php://input', 'r'));

$result = $s3->putObject(array(
    'Bucket' => $bucket,
    'Key' => $keyname,
    'ContentLength' => (int)$_SERVER['CONTENT_LENGTH'],
    'Body' => $stream,
    'ACL' => 'private',
    'StorageClass' => 'STANDARD_IA',
));
Run Code Online (Sandbox Code Playgroud)

就如此容易。