【问题标题】:How to flush output after each `echo` call?每次“回声”调用后如何刷新输出?
【发布时间】:2011-03-09 04:09:34
【问题描述】:

我有一个只向客户端生成日志的 php 脚本。
当我回显某些内容时,我希望将其即时传输到客户端。
(因为在处理脚本时,页面是空白的)
我已经玩过ob_start()ob_flush(),但是它们没有用。

什么是最好的解决方案?

PS:在echo 调用结束时放一个flush有点脏...

编辑:答案都不起作用,PHP 或 Apache 故障?

【问题讨论】:

  • 在飞行中?你的意思是回声,因为它正在产生?因为像 ob_start 这样的输出缓冲函数正好相反。
  • @Manos Dilaverakis - 我的意思是,当我做回声时,我不希望它直接传输到客户端,目前,页面正在加载,并且仅在脚本末尾,它一次全部转移。猫是 apache 有错吗?
  • 这是预期的行为 - 一旦脚本停止执行,就会发送输出,除非您另有指定。根据您的应用程序的设计方式,您可能能够在执行的某些点刷新缓冲区(例如,当一个类被实例化或一个经常被调用的给定视图函数运行时)。

标签: php apache echo flush


【解决方案1】:

我遇到了同样的问题,并且手册中发布的示例之一有效。必须将字符集指定为此处已经提到的海报之一。 http://www.php.net/manual/en/function.ob-flush.php#109314

header( 'Content-type: text/html; charset=utf-8' );
echo 'Begin ...<br />';
for( $i = 0 ; $i < 10 ; $i++ )
{
    echo $i . '<br />';
    flush();
    ob_flush();
    sleep(1);
}
echo 'End ...<br />';

【讨论】:

  • 我尽一切努力让输出刷新正常工作。最终让它工作的是你的例子中的“内容类型”标题。谢谢你好先生!!!
  • 是的,也为我解决了这个问题。惊人的。虽然,我还必须启用隐式刷新,但无论如何。 :)
  • 这里的例子和其他页面都不适合我。Linux with Apache and php 7
  • 我发现如果第一个echo是一个标签,它似乎失败了。就像echo '&lt;h1&gt;Begin...&lt;/h1&gt;'; 一样,第一个回显会导致整个事情等到它完成。使用此处的确切示例在 centos、apache 和 php 7 上运行良好。
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案2】:

编辑:

我正在阅读手册页上的 cmets,发现 a bug 指出 ob_implicit_flush 不起作用,以下是解决方法:

ob_end_flush();

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_start();

如果这不起作用,那么甚至可能发生的情况是客户端没有从服务器接收数据包,直到服务器建立足够的字符来发送它认为值得发送的数据包。


旧答案:

您可以使用ob_implicit_flush 告诉输出缓冲关闭缓冲一段时间:

ob_implicit_flush(true);

# CODE THAT NEEDS IMMEDIATE FLUSHING

ob_implicit_flush(false);

【讨论】:

  • 是的,我确实在寻找类似的东西 (+1),但它没有用:s 可能是 Apache 的错吗?
  • 如果我停止刷新ob_end_flush(),我的程序会跳过重要的步骤(不知道为什么;s)我在我的 Class DebugEcho 函数中添加了行 flush,并且它也不起作用,此外,每次echo 调用时刷新缓存有点糟糕且耗时。
  • ob_end_flush 将清除缓冲区并完全停止输出缓冲,所以在确定不想再缓冲内容之前不要使用它!
  • 您仍然需要在每行输出之后flush(),因为它不能保证立即发送到客户端。
【解决方案3】:

这就是我发现的。

Flush 不能在 Apache 的 mod_gzip 或 Nginx 的 gzip 下工作,因为从逻辑上讲,它是对内容进行 gzip 压缩,为此它必须缓冲内容以对其进行 gzip。任何类型的 Web 服务器 gzipping 都会影响这一点。简而言之,在服务器端,我们需要禁用 gzip 并减小 fastcgi 缓冲区大小。所以:

  • 在 php.ini 中:

    output_buffering = Off
    
    zlib.output_compression = Off
    
  • 在 nginx.conf 中:

    gzip  off;
    
    proxy_buffering  off;
    

手头也有这些行,特别是如果您无权访问 php.ini:

@ini_set('zlib.output_compression',0);

@ini_set('implicit_flush',1);

@ob_end_clean();

set_time_limit(0);

最后,如果你有,请在下面评论代码:

ob_start('ob_gzhandler');

ob_flush();

PHP测试代码:

ob_implicit_flush(1);

for ($i=0; $i<10; $i++) {
    echo $i;

    // this is to make the buffer achieve the minimum size in order to flush data
    echo str_repeat(' ',1024*64);

    sleep(1);
}

【讨论】:

  • 对我来说答案的关键是//this is for the buffer achieve the minimum size in order to flush data echo str_repeat(' ',1024*64);
  • 只有这对我有用。为 str_repeat 位 +1,但将其设为不可见字符而不是空格可能是个好主意?
  • 那么我们如何只为那个页面禁用 gzip 呢?
  • echo str_repeat(' ',1024*64); 使flush 工作。但是我怎么在别的地方找不到呢?文档也没有提到它......
  • 这是确认成功的终极神级步骤。在“回声 str_repeat(' ',1024*64);”我不得不使用 4096 而不是 1024 以获得更好的实时回声。欢呼
【解决方案4】:

对于 2018 年即将到来的人:

唯一对我有用的解决方案:

<?php

    if (ob_get_level() == 0) ob_start();
    for ($i = 0; $i<10; $i++){

        echo "<br> Line to show.";
        echo str_pad('',4096)."\n";    

        ob_flush();
        flush();
        sleep(2);
    }

    echo "Done.";

    ob_end_flush();
?>

保留“4096”部分非常重要,因为它似乎“填充”了缓冲区...

【讨论】:

  • 2020 年表现不佳;前 4 行在 4 秒后全部显示一次,然后在此之后,每个新行每秒显示一个。
  • 但是如果我增加 str_pad 值,效果会更好! 4096000 显示得很好。它绝对是浏览器处理的内容值。
  • 每一步发送 4MB 的文本数据是不是有点矫枉过正?
  • @M.i.X 是的!但这是唯一的方法(我知道)使用 php 来做到这一点......正确的方法应该是 ajax,但如果你只想使用 php,我不知道更好的方法
  • 我用 4096 但没用,改成 1024*64 就可以了。我不知道为什么。
【解决方案5】:

刷新似乎无法正常工作是自动字符集检测的副作用。

浏览器在知道要显示它的字符集之前不会显示任何内容,如果您不指定字符集,它需要尝试猜测它。问题在于,如果没有足够的数据,它就无法做出正确的猜测,这就是为什么浏览器似乎需要在显示任何内容之前填充这个 1024 字节(或类似)的缓冲区。

因此,解决方案是确保浏览器不必猜测字符集。

如果您要发送文本,请添加一个 '; charset=utf-8' 为其内容类型,如果是 HTML,则将字符集添加到相应的元标记中。

【讨论】:

  • 这正是我想要的!谢谢。
  • 不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案6】:

你想要的是flush 方法。 示例:

echo "log to client";
 flush();

【讨论】:

  • @CuSS 无论如何,这是满足您想要的功能的唯一方法
  • 它有什么不妥之处?这就是 flush() 的设计目的,它完成了工作。
  • ini_set('implicit_flush', true) 基本上会在输出块之后模拟一个flush(),它几乎可以在任何地方设置(php.ini、.htaccess、per-script 等...)跨度>
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案7】:

为什么不做一个回显函数,像这样:

function fecho($string) {
 echo $string;
 ob_flush();
}

【讨论】:

  • 比@GSto 答案更好,但我正在寻找类似@amphetamachine 的答案...你会得到+1 ;)
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案8】:

我也有类似的事情要做。使用

// ini_set("output_buffering", 0);  // off 
ini_set("zlib.output_compression", 0);  // off
ini_set("implicit_flush", 1);  // on   

在我的情况下确实使输出刷新频繁。

但我必须在特定点(在我运行的循环中)刷新输出,所以同时使用

ob_flush();
flush();

一起为我工作。

我做不到 关闭 "output_buffering" ini_set(...),只好直接转 在 php.ini 中,phpinfo() 显示了它的设置 关闭时作为“没有价值”,是 普通的? .

【讨论】:

  • 是的,如果 phpinfo() 显示“无值”,则表示 output_buffering 设置为关闭。否则它会说像 4096
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案9】:

不常提及的一件事是 gzip 压缩由于各种托管环境中的细节而一直处于开启状态。

这是一种现代方法,使用 PHP-FPM 作为快速 CGI,不需要 .htaccess 重写规则或环境变量:

在 php.ini 或 .user.ini 中:

output_buffering = 0
zlib.output_compression = 0
implicit_flush = true
output_handler = 

在 PHP 脚本中:

header('Content-Encoding: none'); // Disable gzip compression
ob_end_flush(); // Stop buffer
ob_implicit_flush(1); // Implicit flush at each output command

有关 ob_end_flush() 的需要,请参阅 PHP 官方文档中的 this comment

【讨论】:

  • 这是唯一对我有用的解决方案,我尝试了所有方法。谢谢!
  • 2020 年表现不佳;前 4 行在 4 秒后全部显示一次,然后在此之后,每个新行每秒显示一个
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案10】:

正确使用的函数是flush()

<html>
<body>
<p>
Hello! I am waiting for the next message...<br />
<?php flush(); sleep(5); ?>
I am the next message!<br />
<?php flush(); sleep(5); ?>
And I am the last message. Good bye.
</p>
</body>
</html>

请注意,IE 存在一个“问题”,它仅在至少 256 字节时才输出刷新的内容,因此您的页面第一部分至少需要 256 字节。

【讨论】:

  • 酷,不知道那个 IE 错误...除了echoprint 之外,还有直接打印到浏览器的功能吗?
  • @CuSS:这不是错误;这是一个“未记录的功能”
  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案11】:

这对我来说很好(Apache 2.4/PHP 7.0):

@ob_end_clean();
echo "lorem ipsum...";
flush();
sleep(5);
echo "<br>dolor...";
flush();
sleep(5);
echo "<br>sit amet";

【讨论】:

  • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
【解决方案12】:

防病毒软件也可能会干扰输出刷新。就我而言,卡巴斯基反病毒软件 2013 在将数据块发送到浏览器之前会保存数据块,即使我使用的是公认的解决方案。

【讨论】:

    【解决方案13】:
    header( 'X-Accel-Buffering: no' );
    header( 'Content-Type: text/html; charset=utf-8' );
    
    echo 'text to display';
    echo '<span style="display: none;">' . str_repeat ( ' ', 4096 ) . '</span>';
    flush();
    usleep( 10 );
    

    【讨论】:

      【解决方案14】:

      有时,问题来自 Apache 设置。 Apache 可以设置为 gzip 输出。 例如,您可以在文件 .htaccess 中添加:

      SetEnv no-gzip 1
      

      【讨论】:

        【解决方案15】:

        试试这个:

        while (@ob_end_flush());      
        ob_implicit_flush(true);
        
        echo "first line visible to the browser";
        echo "<br />";
        
        sleep(5);
        
        echo "second line visible to the browser after 5 secs";
        

        请注意,这种方式实际上是在禁用当前脚本的输出缓冲区。我想你可以用 ob_start() 重新启用它(我不确定)。

        重要的是,通过像上面那样禁用输出缓冲区,您将无法再使用header() 函数重定向您的 php 脚本,因为 php 在每次脚本执行时只能发送一次 http 标头。 但是,您可以使用 javascript 进行重定向。让你的 php 脚本回显以下几行:

                echo '<script type="text/javascript">';
                echo 'window.location.href="'.$url.'";';
                echo '</script>';
                echo '<noscript>';
                echo '<meta http-equiv="refresh" content="0;url='.$url.'" />';
                echo '</noscript>'; 
                exit;
        

        【讨论】:

        • 代码不起作用。在 Awardspace 免费托管默认设置、firefox 上进行了测试。
        【解决方案16】:

        请注意,如果您在 Dreamhost 等某些共享托管网站上,则根本无法禁用 PHP 输出缓冲,而无需通过不同的路径:

        更改输出缓冲区缓存 如果您使用的是 PHP FastCGI,PHP 函数 flush()、ob_flush() 和 ob_implicit_flush() 不会 发挥预期的作用。默认情况下,输出在更高级别缓冲 比 PHP (具体来说,由 Apache 模块 mod_deflate 是 形式/功能类似于 mod_gzip)。

        如果您需要无缓冲的输出,则必须使用 CGI(而不是 FastCGI)或联系支持人员请求禁用 mod_deflate 为您的网站。

        https://help.dreamhost.com/hc/en-us/articles/214202188-PHP-overview

        【讨论】:

          【解决方案17】:

          我迟到了讨论,但我读到很多人都在说附加 flush();每个代码的末尾看起来很脏,它们是对的。

          最佳解决方案是禁用 deflate、gzip 以及来自 Apache、中间处理程序和 PHP 的所有缓冲。然后在你的 php.ini 中你应该有:

                      output_buffering = Off
                      zlib.output_compression = Off
                      implicit_flush = Off
          

          临时解决方案是在你的 php.ini IF 中有这个,你可以用 flush(); 解决你的问题;但你觉得随处可见又脏又丑。

          implicit_flush = On
          

          如果你只把它放在你的php.ini中,你不需要把flush();不再在你的代码中了。

          【讨论】:

            【解决方案18】:

            这是我的代码:(适用于 PHP7)

            private function closeConnection()
            {
                @apache_setenv('no-gzip', 1);
                @ini_set('zlib.output_compression', 0);
                @ini_set('implicit_flush', 1);
            
                ignore_user_abort(true);
                set_time_limit(0);
            
                ob_start();
                // do initial processing here
                echo json_encode(['ans' => true]);
            
                header('Connection: close');
                header('Content-Length: ' . ob_get_length());
                ob_end_flush();
                ob_flush();
                flush();
            }
            

            【讨论】:

              猜你喜欢
              • 2016-05-07
              • 2010-09-17
              • 2010-12-04
              • 1970-01-01
              • 2015-11-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多