php SteamedResponse未在刷新时输出

wlwcrazw  于 2023-09-29  发布在  PHP
关注(0)|答案(1)|浏览(117)

所以我在Symfony PHP中编写了一个脚本,我需要读取多个响应流,然后将它们流回客户端。相关端点位于nginx代理后面。
正在阅读流:

$fileHandle = fopen('php://temp', 'w+');

    // Implements StreamableInterface
    foreach ($responses as $response) {
        try {
            $stream = $response->toStream();
            while (!feof($stream)) {
                fwrite($fileHandle, fread($stream, 4096));
            }
        } catch (TransportExceptionInterface $e) {
            continue;
        }
        unset($response);
    }

我在这里捕获了一个TransportException,主要是为了防止从Nginx接收到504 Gateway Timeout。在这种情况下,我们将像没有抛出异常一样继续前进。我还尝试将此位更改为stream_copy_to_stream,但得到了类似的结果
当试图将此文件服务回客户端时,问题就出现了。中间缺少一些步骤,但这是一般要点:

rewind($fileHandle);

        $streamedResponse = new StreamedResponse();
        $streamedResponse->setCallback(function() use ($fileHandle) {
            while(!feof($fileHandle)) {
                echo fread($fileHandle, 1024);
                // ob_flush();
                flush();
            }
            fclose($fileHandle);
        });

        $streamedResponse->headers->set('Content-Type', 'application/force-download');
        $streamedResponse->headers->set(
            'Content-Disposition',
            'attachment; filename=' . $fileName
        );
        $streamedResponse->headers->set('Content-Type', 'text/csv; charset=UTF-8');
        $streamedResponse->headers->set('X-Accel-Buffering', 'no');

如果我只是回显/转储fread行,我可以在Symfony的分析器中看到所有内容,或者发出请求的选项卡。然而,一旦我调用flush(),我就不会得到任何文件,我再也不能查看脚本的任何输出,分析器中没有条目,没有转储,没有日志,几乎就像进程刚刚完全停止一样。
我只能在工作流早期抛出504时复制此操作,但我真的看不出这对发送回客户端的响应有什么影响。我也尝试了ob_start和ob_flush的不同组合,似乎没有什么能解决这个问题。
任何帮助将不胜感激。

wdebmtf2

wdebmtf21#

您的方法的问题在于,在发送流响应之前,必须首先读取所有响应。它正在吞噬大量的记忆。更好的方法是直接从回调函数读取和写入输出。您可以使用php://outputstream_copy_to_stream来代替您提到的阅读部分流。

// To be adapt to your project

use Symfony\Component\HttpFoundation\HeaderUtils; // Used later

$streamedResponse = new StreamedResponse();
$streamedResponse->setCallback(function() use ($fileHandle, $responses) {
    $fileHandle = fopen('php://output', 'wb');
    foreach ($responses as $response) {
        try {
            $stream = $response->toStream();
            stream_copy_to_stream($stream, $fileHandle);
        } catch (TransportExceptionInterface $e) {
            continue;
        }
    }
});

// Useless as redeclared below $streamedResponse->headers->set('Content-Type', 'application/force-download');
$disposition = HeaderUtils::makeDisposition( HeaderUtils::DISPOSITION_ATTACHMENT, $fileName);
$streamedResponse->headers->set('Content-Disposition', $disposition);
$streamedResponse->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$streamedResponse->headers->set('X-Accel-Buffering', 'no');

return $streamedResponse;

相关问题