所以我在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的不同组合,似乎没有什么能解决这个问题。
任何帮助将不胜感激。
1条答案
按热度按时间wdebmtf21#
您的方法的问题在于,在发送流响应之前,必须首先读取所有响应。它正在吞噬大量的记忆。更好的方法是直接从回调函数读取和写入输出。您可以使用
php://output
和stream_copy_to_stream
来代替您提到的阅读部分流。