【问题标题】:Stream remote file with PHP and Guzzle使用 PHP 和 Guzzle 流式传输远程文件
【发布时间】:2016-12-06 18:05:15
【问题描述】:

我的应用程序应该将一个大文件流回浏览器,即远程服务器。目前该文件由本地 NodeJS 服务器提供。

我正在使用 25GB 的 VirtualBox 磁盘映像,只是为了确保在流式传输时它不会存储在内存中。 这是我正在努力解决的相关代码

    require __DIR__ . '/vendor/autoload.php';
    use GuzzleHttp\Stream\Stream;
    use GuzzleHttp\Stream\LimitStream;

    $client = new \GuzzleHttp\Client();
    logger('==== START REQUEST ====');
    $res = $client->request('GET', 'http://localhost:3002/', [
      'on_headers' => function (\Psr\Http\Message\ResponseInterface $response) use ($res) {
        $length = $response->getHeaderLine('Content-Length');
        logger('Content length is: ' . $length);
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="testfile.zip"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . $length);

      }
    ]);

    $body = $res->getBody();
    $read = 0;
    while(!$body->eof()) {
      logger("Reading chunk. " . $read);
      $chunk = $body->read(8192);
      $read += strlen($chunk);
      echo $chunk;
    }
    logger('Read ' . $read . ' bytes');
    logger("==== END REQUEST ====\n\n");

    function logger($string) {
      $myfile = fopen("log.txt", "a") or die ('Unable to open log file');
      fwrite($myfile, "[" . date("d/m/Y H:i:s") . "] " . $string . "\n");
      fclose($myfile);
    }

即使$body = $res->getBody(); 应该返回一个流,它也会很快用交换数据填满磁盘,这意味着它会在流回客户端之前尝试将其保存在内存中,但这不是预期的行为。我错过了什么?

【问题讨论】:

    标签: php streaming guzzle


    【解决方案1】:

    您必须像这样指定streamsink 选项:

    $res = $client->request('GET', 'http://localhost:3002/', [
        'stream' => true,
        'sink' => STDOUT, // Default output stream.
        'on_headers' => ...
    ]);
    

    在这些添加之后,您将能够逐块流式传输响应,而无需任何额外的代码从响应主体流复制到 STDOUT(使用echo)。

    但通常您不想这样做,因为您需要为每个活动客户端拥有一个 PHP 进程(php-fpm 或 Apache 的 mod_php)。

    如果您只想提供机密文件,请尝试使用“内部重定向”:通过 nginx 的 X-Accel-Redirect 标头或 Apache 的 X-Sendfile。您将获得相同的行为,但使用的资源更少(因为在 nginx 的情况下高度优化了事件循环)。有关配置的详细信息,您可以阅读官方文档,当然也可以阅读其他 SO 问题(例如 this one)。

    【讨论】:

    • stream => true 提示按我的预期工作!出于我们的目的,这可能就足够了(我们预计不会有多少客户端同时活动),但我会仔细阅读链接的线程,以便我也可以尝试“更安全”的替代方案。无论如何,很棒的答案! ?
    猜你喜欢
    • 2010-12-26
    • 2011-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-27
    • 2013-06-01
    • 1970-01-01
    相关资源
    最近更新 更多