yii框架的响应流式输出_streamresponse发送大文件【技巧】

StreamResponse 为什么发不出大文件?

因为默认配置下,PHP 的 output_buffering 和 Yii 的中间件(比如 Response 自动 setContentLength)会把整个文件读进内存再吐出去,根本没走流式逻辑。你看到的“卡住”“502”“内存爆掉”,基本都卡在这儿。

必须手动禁用输出缓冲:ob_end_clean() 或设 output_buffering = Off in php.iniStreamResponse 不会自动设置 Content-Length,但某些代理或浏览器(尤其 Safari)没它就不触发下载进度条——得自己算并调用 $response->headers->set(‘Content-Length’, $size)别在 beforeAction 或过滤器里提前写响应体,否则 StreamResponse 的底层 fopen(‘php://output’) 会失败

怎么让 StreamResponse 真正“边读边发”?

核心是绕过 Yii 默认的响应体收集机制,直接接管输出流。不是 new 一个 StreamResponse 就完事,得让它成为最终返回值,并且不被其他层拦截。

控制器里必须 return 它,不能只 $response->send(): return Yii::$app->response->stream(function () use ($filePath) { $fp = fopen($filePath, ‘rb’); while (!feof($fp)) { echo fread($fp, 8192); flush(); // 关键,不然 Web 服务器可能缓存 } fclose($fp);}, [‘contentType’ => ‘application/octet-stream’]);确保 flush() 生效:Nginx 需关 fastcgi_buffering off;;Apache 要关 mod_deflate 或加 SetEnv no-gzip 1避免使用 Yii::info() 或日志写入响应过程中,file_put_contents 或 error_log 可能触发隐式 flush 干扰流

404 或断连时文件不完整?检查这些点

流式传输没有“事务”,一旦连接中断,客户端就拿到半截文件,而服务端还傻等 fclose —— 这时候靠 PHP 的 connection_aborted() 和 ignore_user_abort(false) 来收尾。

在 stream callback 里每读一段后加判断:if (connection_status() !== CONNECTION_NORMAL) { return; }别设 set_time_limit(0) 就万事大吉,PHP-FPM 的 request_terminate_timeout 仍会杀进程,建议设为略大于文件最大传输时间如果用 X-Sendfile 或 X-Accel-Redirect,StreamResponse 根本不生效——那是 Web 服务器直读文件,和 PHP 流式无关,别混用

Content-Disposition 中文名乱码怎么办?

HTTP 头不支持 UTF-8 原样传,filename=中文.pdf 在 Chrome 可能变成下划线,Firefox 直接拒载。得按 RFC 5987 编码。

用 rawurlencode() + mb_convert_encoding() 构造:filename*=UTF-8”%E4%BD%A0%E5%A5%BD.pdf别只设 filename,要同时设 filename(ASCII fallback)和 filename*(RFC 5987)两个 headerYii 的 HeaderCollection::set() 不自动处理多值,得手写:$response->headers->set(‘Content-Disposition’, ‘attachment; filename="report.pdf"; filename*=UTF-8\’\’%E6%8A%A5%E8%A1%A8.pdf’);流式输出真正难的不是写几行 callback,而是让每一层——PHP、Web 服务器、反向代理、浏览器——都不插手、不缓存、不重写、不猜意图。漏掉任意一环,文件就卡在 99%。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。