我有一个发送大量记录的 PHP 脚本,我想在每条记录可用时立即刷新它:客户端能够在每条记录到达时对其进行处理,它不需要等待整个响应。我意识到整个传输需要更长的时间,因为它需要以多个数据包的形式发送,但它仍然允许客户端更快地开始工作。
我已经尝试了所有不同的flush()
功能ob_flush()
,但似乎没有什么能帮助在页面完成之前通过线路实际发送数据。我已经确认它不是网络浏览器,因为我已经使用 telnet 对其进行了测试。
我有一个发送大量记录的 PHP 脚本,我想在每条记录可用时立即刷新它:客户端能够在每条记录到达时对其进行处理,它不需要等待整个响应。我意识到整个传输需要更长的时间,因为它需要以多个数据包的形式发送,但它仍然允许客户端更快地开始工作。
我已经尝试了所有不同的flush()
功能ob_flush()
,但似乎没有什么能帮助在页面完成之前通过线路实际发送数据。我已经确认它不是网络浏览器,因为我已经使用 telnet 对其进行了测试。
对我有用的唯一解决方案是将output_buffering
php.ini 中的指令设置为“关闭”。我不想为整个服务器执行此操作,只是针对这一特定资源。通常您可以ini_set
从 PHP 脚本中使用,但无论出于何种原因 php 都不允许output_buffering
以这种方式设置(请参阅php 手册)。
事实证明,如果您使用的是 Apache,您可以output_buffering
从您的服务器配置中设置一些 php ini 指令(包括 .htaccess 文件)。因此,我在 .htaccess 文件中使用了以下内容来仅为该文件禁用 output_buffering:
<Files "q.php">
php_value output_buffering Off
</Files>
然后在我的静态服务器配置中,我只需要AllowOverride Options=php_value
(或更大的锤子,如AllowOverride All
)以便在 .htaccess 文件中允许这样做。
你没有提到你正在使用什么网络服务器,但我会在这里冒险并猜测 Apache2。我击中了您描述的几乎相同的东西。我试图让我的 cgi 脚本在它准备好时传回信息,而不是缓冲整个事情。在 curl 等中快速工作,但在浏览器(几乎所有浏览器)中缓冲,这至少令人抓狂。我经历了你描述的确切步骤。在我的情况下,解决方案是修改sites-enabled/terrifico.com
Apache2 中的配置文件(有问题的行以
SetEnvIfNoCase
(您可以忽略该行上方和下方的内容,我只是将其显示为我放置位置的参考。)
<VirtualHost *:80>
ServerAdmin webmaster@localhost
ServerName test.terrifico.com
ServerAlias test.terrifico.com
SetEnvIfNoCase Request_URI \.cgi$ no-gzip dont-vary
DocumentRoot /var/www/test.terrifico.com
从盯着来回的网络流量看,我终于意识到浏览器正在宣传它接受任何东西(即文本)的通货紧缩。例如,这就是浏览器和 curl 之间的区别。突出的一点是
接受编码:gzip、deflate、sdch
有一点关于chunking
,但这并没有影响这个特定的问题。因此,浏览器请求mod_deflate
启动,这使我在 cgi 脚本中小心地吐出字节时失败了。您可以在浏览器中更改它,但在服务器上更改一次似乎更明智。
也许这会有所帮助。
要在 PHP 运行时关闭输出缓冲而不更改php.ini
或拥有.htaccess
文件,只需在脚本开头使用ob_end_flush()
或。ob_end_clean()
例如:
这应该在没有缓冲的情况下输出:
<?php
ob_end_clean();
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
flush();
usleep(0.5e6);
}
如果output_buffering
打开,无论flush()
调用如何,都会输出缓冲(一次全部):
<?php
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
flush();
usleep(0.5e6);
}
尽管它的名字,ob_implicit_flush
调用flush()
,而不是ob_flush()
,在每个输出后隐含。在开始关闭输出缓冲区后,这在这种情况下会很方便:
<?php
ob_end_clean(); // disable output buffer
ob_implicit_flush(); // call flush() automatically after every output
for ($i = 0; $i < 5; $i++)
{
echo "$i\n";
usleep(0.5e6);
}
这修复了 PHP 方面。可能还有其他事情发生mod_deflate
或类似情况(请参阅 Ted Collins 的回答),我观察到 Firefox 至少需要 1024 字节才能开始输出任何内容。