PHP中flush()函数在Safari中无效的原因及解决办法
实时输出PHP耗时脚本内容的跨浏览器兼容方案
我有一个耗时的PHP脚本,需要向用户提供实时反馈。简化后的脚本在Firefox中能即时刷新输出,但Safari要等所有内容生成后才显示,Chrome直接报错ERR_CONTENT_DECODING_FAILED。
原脚本代码
<?php ini_set('max_execution_time', 0); header('Content-Encoding: none;'); header('Content-type: text/html; charset=utf-8'); $j = 8; $k = pow(2, 10); echo "One moment please...".str_pad('', $k)."<br /> <br /> "; flush(); $i = 0; while ($i < $j) { $i++; echo "Test ".$i.str_pad('',$k)."<br /> "; flush(); sleep(1); } ?>
已尝试的无效操作
- 在.htaccess中添加
SetEnv no-gzip 1或SetEnv no-gzip dont-vary - 开启/关闭
output_buffering - 设置
ob_start(null, 4096) - 关闭
zlib.output_compression - 添加
Cache-Control、X-Content-Type-Options、X-Accel-Buffering等响应头 - 调整
str_pad的长度 - 在
flush()前后添加@ob_flush()
针对PHP 8.1+FastCGI环境的解决方案
关键修复步骤
移除错误的编码头
原脚本中header('Content-Encoding: none;');是Chrome报错的直接原因,直接删除该行即可解决解码失败问题。强制关闭全链路输出缓冲
在脚本开头添加代码,确保PHP层面和所有已启动的输出缓冲区都被关闭:ini_set('output_buffering', 'off'); ini_set('zlib.output_compression', false); while (ob_get_level() > 0) { ob_end_flush(); }添加FastCGI专属禁用缓冲头
FastCGI环境下必须通过X-Accel-Buffering: no告知服务器不要缓冲输出:header('X-Accel-Buffering: no');确保浏览器触发刷新
保持str_pad生成足够长度的内容(1KB以上),同时每次输出后必须执行ob_flush()+flush()组合操作,确保内容被推送到浏览器。
完整修复后的脚本
<?php ini_set('max_execution_time', 0); ini_set('output_buffering', 'off'); ini_set('zlib.output_compression', false); // 关闭所有已开启的输出缓冲区 while (ob_get_level() > 0) { ob_end_flush(); } header('Content-type: text/html; charset=utf-8'); header('X-Accel-Buffering: no'); header('Cache-Control: no-cache, no-store, must-revalidate'); header('Pragma: no-cache'); header('Expires: 0'); $j = 8; $k = pow(2, 10); echo "One moment please..." . str_pad('', $k) . "<br>\n<br>\n"; ob_flush(); flush(); $i = 0; while ($i < $j) { $i++; echo "Test " . $i . str_pad('', $k) . "<br>\n"; ob_flush(); flush(); sleep(1); } ?>
额外说明
- FastCGI环境下,
.htaccess的压缩禁用配置可能被服务器全局设置覆盖,必须通过PHP代码直接禁用压缩和缓冲。 - Safari对输出内容的触发阈值更高,1KB的空白填充足够让浏览器实时渲染新内容。
内容的提问来源于stack exchange,提问作者Sanderadio
相关产品推荐
相关产品推荐

