You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Guzzle通过PUT请求分块写入请求体?

如何用Guzzle分块写入PUT请求的请求体

问题描述

我想使用Guzzle发起PUT请求并在请求体中发送数据,请问怎么像读取响应体那样分块写入请求体?

参考的响应体读取代码

while (!$body->eof()) 
{
    $data = $body_origin->read(1024*1024);
}

我尝试的方案

$dest_stream = Psr7\Utils::streamFor();
$response_dest = $client_destination->request('PUT', $url_dest, 
[
    'stream' => true,
    'body' => $dest_stream,
    'read_timeout' => 100,
    'Content-Type' => 'application/octet-stream',
    'headers' => 
    [
        'Content-Length' => '0'
    ]
]);

while (!$data_to_send->eof()) 
{
    $data = $data_to_send->read(1024*1024);
    $dest_stream->write($data);
}

解决方案

你当前的写法存在逻辑问题:调用request()方法后,Guzzle会立即发送请求,这时候再往$dest_stream里写入数据已经晚了,请求已完成,流内的内容不会被发送。

正确的思路是让Guzzle在发送请求时按需读取数据源,把分块读取$data_to_send的逻辑绑定到请求体流的读取操作上。下面是两种可行的实现方式:

方式一:使用可回调的流(推荐)

利用Psr7\Utils::streamFor()接收回调函数,Guzzle在需要请求体数据时会自动调用该回调获取数据:

use GuzzleHttp\Psr7\Utils;

// $data_to_send为你的数据源流(如本地文件流、远程响应流等)
$request_body = Utils::streamFor(function () use ($data_to_send) {
    if (!$data_to_send->eof()) {
        return $data_to_send->read(1024 * 1024); // 每次读取1MB
    }
    return false; // 返回false表示数据读取完毕
});

$response_dest = $client_destination->request('PUT', $url_dest, [
    'read_timeout' => 100,
    'Content-Type' => 'application/octet-stream',
    // 无需手动设置Content-Length,Guzzle会自动处理:
    // 若数据源是可查找的流,自动计算长度;否则使用Transfer-Encoding: chunked分块传输
]);

方式二:自定义PSR-7流

如果需要更精细的控制,可以实现自定义PSR-7流类,将分块读取逻辑封装在read()方法中:

use Psr\Http\Message\StreamInterface;

class ChunkedRequestBody implements StreamInterface
{
    private $source;
    private $chunkSize;

    public function __construct(StreamInterface $source, int $chunkSize = 1048576)
    {
        $this->source = $source;
        $this->chunkSize = $chunkSize;
    }

    // 核心实现:分块读取数据源
    public function read($length)
    {
        return $this->source->read($this->chunkSize);
    }

    // 以下方法直接委托给数据源流实现
    public function eof() { return $this->source->eof(); }
    public function getContents() { return $this->source->getContents(); }
    public function close() { return $this->source->close(); }
    public function detach() { return $this->source->detach(); }
    public function getSize() { return $this->source->getSize(); }
    public function tell() { return $this->source->tell(); }
    public function isSeekable() { return $this->source->isSeekable(); }
    public function seek($offset, $whence = SEEK_SET) { return $this->source->seek($offset, $whence); }
    public function rewind() { return $this->source->rewind(); }
    public function isWritable() { return false; }
    public function write($string) { throw new \RuntimeException('Cannot write to this stream'); }
    public function isReadable() { return true; }
    public function getMetadata($key = null) { return $this->source->getMetadata($key); }
}

// 使用自定义流
$request_body = new ChunkedRequestBody($data_to_send);

$response_dest = $client_destination->request('PUT', $url_dest, [
    'read_timeout' => 100,
    'Content-Type' => 'application/octet-stream',
]);

注意事项

  • 不要手动设置Content-Length: 0,这会告知服务器请求体为空,后续写入的数据不会生效。
  • stream => true参数用于流式读取响应体,和请求体无关,无需设置。

内容的提问来源于stack exchange,提问作者Danielzt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 23:42:18