php 刷新不工作

发布于 2024-10-12 10:12:20 字数 353 浏览 4 评论 0原文

<?php
for($i=0;$i<20;$i++)
{
    echo 'printing...<br />';
    ob_flush();
    flush();

    usleep(300000);
}

?>

包含代码的网址: http://domainsoutlook.com/sandbox/delayed.php

我有一个专用服务器,这样我就可以进行更改。我正在运行 apache 和 nginx 作为代理服务器。

<?php
for($i=0;$i<20;$i++)
{
    echo 'printing...<br />';
    ob_flush();
    flush();

    usleep(300000);
}

?>

Url that contains the code: http://domainsoutlook.com/sandbox/delayed.php

I have a dedicated server so I can make the changes. I am running apache and nginx as the proxy server.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

枯叶蝶 2024-10-19 10:12:20

这就是我发现的:

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

  • 在 php.ini 中:

    。输出缓冲 = 关闭

    。 zlib.output_compression = Off

  • 在 nginx.conf 中:

    。 gzip 关闭;

    。 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 for the buffer achieve the minimum size in order to flush data
    echo str_repeat(' ',1024*64);

    sleep(1);
}

So that's what I found out:

Flush would not work under Apache's mod_gzip or Nginx's gzip because, logically, it is gzipping the content, and to do that it must buffer content to gzip it. Any sort of web server gzipping would affect this. In short, at the server side, we need to disable gzip and decrease the fastcgi buffer size. So:

  • In php.ini:

    . output_buffering = Off

    . zlib.output_compression = Off

  • In nginx.conf:

    . gzip off;

    . proxy_buffering off;

Also have this lines at hand, specially if you don't have acces to php.ini:

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

  • @ini_set('implicit_flush',1);

  • @ob_end_clean();

  • set_time_limit(0);

Last, if you have it, coment the code bellow:

  • ob_start('ob_gzhandler');

  • ob_flush();

PHP test code:

ob_implicit_flush(1);

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

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

    sleep(1);
}
不再见 2024-10-19 10:12:20

您使用的是 ob_flush 而没有 ob_start,所以没有什么可以刷新的。

它还取决于网络服务器和代理及其设置。

您应该禁用 Nginx 的缓冲(将 proxy_buffering off; 添加到配置文件并重新启动 Nginx)

此外,检查您的 php.ini 是否包含 output_buffering = Off和zlib.output_compression = Off

You're using ob_flush without ob_start, so there is nothing to flush for it.

It also depends on the webserver and proxy and its settings.

You should disable buffering for Nginx (add proxy_buffering off; to the config file and restart Nginx)

Also, check if your php.ini contains output_buffering = Off and zlib.output_compression = Off.

岁月静好 2024-10-19 10:12:20

主php文件;

<?php
header('Content-Type: text/HTML; charset=utf-8');
header( 'Content-Encoding: none; ' );//disable apache compressed
session_start();
ob_end_flush();
ob_start();
set_time_limit(0);
error_reporting(0);

..... bla bla

for(each)........
{
   bla bla..
    echo "<br>>>>".$i."<<<br>";
    ob_flush();
    flush(); //ie working must

}
?>

它正在工作..

Main php file;

<?php
header('Content-Type: text/HTML; charset=utf-8');
header( 'Content-Encoding: none; ' );//disable apache compressed
session_start();
ob_end_flush();
ob_start();
set_time_limit(0);
error_reporting(0);

..... bla bla

for(each)........
{
   bla bla..
    echo "<br>>>>".$i."<<<br>";
    ob_flush();
    flush(); //ie working must

}
?>

it's working..

花之痕靓丽 2024-10-19 10:12:20

您必须填充缓冲区,以便将其刷新到浏览器。
echo 完整代码后使用此

echo str_pad('',4096)."\n";

<?php
     if (ob_get_level() == 0) ob_start();

     for( $i=0 ; $i<20 ; $i++) {
        echo 'printing...<br />';
        echo str_pad('',4096)."\n";

        ob_flush();
        flush();

        usleep(300000);
     }
     ob_end_flush();
?>

You have to fill the buffer, so that it can be flushed to browser.
Use this after echo

echo str_pad('',4096)."\n";

Complete code:

<?php
     if (ob_get_level() == 0) ob_start();

     for( $i=0 ; $i<20 ; $i++) {
        echo 'printing...<br />';
        echo str_pad('',4096)."\n";

        ob_flush();
        flush();

        usleep(300000);
     }
     ob_end_flush();
?>
夜声 2024-10-19 10:12:20

php.ini 中:

output_buffering = Off
zlib.output_compression = Off

nginx.conf 中:

fastcgi_keep_conn on; # < solution
proxy_buffering off;
gzip off;

In php.ini:

output_buffering = Off
zlib.output_compression = Off

In nginx.conf:

fastcgi_keep_conn on; # < solution
proxy_buffering off;
gzip off;
一影成城 2024-10-19 10:12:20

正如我所读到的,这似乎是一个非常难以解决的问题,我发现的唯一(肮脏)方法是编写一些无用的输出来填充 ≠ 缓冲区。

  • 没有 SSL
    • 如果没有output_buffering,则需要flush
      nginx 缓冲区可以降低到 PHP 标头大小
    • 使用output_buffering,需要添加ob_flush以具有与上面相同的行为
  • 使用 SSL 相同的行为
    • 还有另一个 SSL 缓冲区,并且 NGX_SSL_BUFSIZE 在 nginx 编译中已修复

这是我的 test.php 文件(使用 ?size=... 调用它来更改在循环中写入空格)

<!DOCTYPE html>
<html>
<head></head>
<body>
<?php
$vars = array('output_buffering', 'zlib.output_compression');
print('<p>');
foreach ($vars as $var) {
  print("$var : ");
  var_dump(ini_get($var));
  print('<br />');
}
print("ob_get_level() : " .ob_get_level());
print('</p>');
if (ob_get_level()) {
  $bytes = ob_get_length();
  ob_flush();
}

$nb_iterations = !empty($_GET['nb']) ? max(2, (int) $_GET['nb']) : 5;
$size = !empty($_GET['size']) ? $_GET['size'] : 0;

for ($i = 1; $i < $nb_iterations; $i++) {
  sleep(1);
  print(str_repeat(' ', 1024 * $size ));
  print("<p>wait $i s</p>");
  if (ob_get_level()) {
    $bytes += ob_get_length();
    print($bytes + strlen($bytes));
    ob_flush(); // this is working, results aren't depending on output_buffering value
  }
  flush(); // this is needed  
}
?>
</body>
</html>

我可以设置的较低conf是

location ~ ^/test.php$ {
  gzip off;
  fastcgi_pass   unix:/var/run/php5-fpm/ssl.socket;
  fastcgi_param   QUERY_STRING            $query_string;  
  fastcgi_param   REQUEST_METHOD          $request_method;
  fastcgi_param   SCRIPT_FILENAME         $request_filename;   
  # if too low => upstream sent too big header while reading response header from upstream
  fastcgi_buffer_size 128; 
  fastcgi_buffers 2 128;  
  fastcgi_busy_buffers_size 128;
}

As I read, it seems a really hard problem to solve, and the only (dirty) way I found is writing something useless to output to fill the ≠ buffers.

  • Without SSL
    • Without output_buffering, flush is needed.
      nginx buffers can be lowered until the PHP header size
    • With output_buffering, ob_flush need to be added to have the same behavior as above
  • With SSL
    • There is another buffer for SSL and NGX_SSL_BUFSIZE is fixed in nginx compilation

Here is my test.php file (call it with ?size=... to change space writing in the loop)

<!DOCTYPE html>
<html>
<head></head>
<body>
<?php
$vars = array('output_buffering', 'zlib.output_compression');
print('<p>');
foreach ($vars as $var) {
  print("$var : ");
  var_dump(ini_get($var));
  print('<br />');
}
print("ob_get_level() : " .ob_get_level());
print('</p>');
if (ob_get_level()) {
  $bytes = ob_get_length();
  ob_flush();
}

$nb_iterations = !empty($_GET['nb']) ? max(2, (int) $_GET['nb']) : 5;
$size = !empty($_GET['size']) ? $_GET['size'] : 0;

for ($i = 1; $i < $nb_iterations; $i++) {
  sleep(1);
  print(str_repeat(' ', 1024 * $size ));
  print("<p>wait $i s</p>");
  if (ob_get_level()) {
    $bytes += ob_get_length();
    print($bytes + strlen($bytes));
    ob_flush(); // this is working, results aren't depending on output_buffering value
  }
  flush(); // this is needed  
}
?>
</body>
</html>

And the lower conf I can set is

location ~ ^/test.php$ {
  gzip off;
  fastcgi_pass   unix:/var/run/php5-fpm/ssl.socket;
  fastcgi_param   QUERY_STRING            $query_string;  
  fastcgi_param   REQUEST_METHOD          $request_method;
  fastcgi_param   SCRIPT_FILENAME         $request_filename;   
  # if too low => upstream sent too big header while reading response header from upstream
  fastcgi_buffer_size 128; 
  fastcgi_buffers 2 128;  
  fastcgi_busy_buffers_size 128;
}
公布 2024-10-19 10:12:20

另一个可能的原因是 mod_security。看起来它有自己的缓冲区。因此,如果您正在使用它,您将必须设置:

SecResponseBodyAccess Off

有点肮脏的解决方法,但到目前为止,这是我让它工作的唯一方法。

Another possible cause is mod_security. It looks like it has it's own buffers. So if you are using it you will have to set :

SecResponseBodyAccess Off

Kind of a dirty workaround but so far that it the only way I got it to work.

木槿暧夏七纪年 2024-10-19 10:12:20

只是想补充一下罗杰的答案。

添加

-flush参数,即

如果您在 Apache2 中使用 FastCGI php5-fpm 模块,您还必须确保在 Apache2 配置中

<IfModule mod_fastcgi.c>
...
        FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -flush -socket /tmp/php5-fpm.sock -idle-timeout 480 -pass-header Authorization
</IfModule>

Just wanted to add to the Roger's answer.

If you are using FastCGI php5-fpm module within Apache2 you must also make sure you are adding

-flush

argument in your Apache2 configuration, i.e.

<IfModule mod_fastcgi.c>
...
        FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -flush -socket /tmp/php5-fpm.sock -idle-timeout 480 -pass-header Authorization
</IfModule>
故人的歌 2024-10-19 10:12:20

检查您的服务器 api

echo phpinfo();

如果您在 CentOS 中找到您的服务器 api

Server API :  CGI/FastCGI

,则在“/etc/httpd/conf.d/fcgid.conf”中添加此行

OutputBufferSize 0

要测试,请重新启动您的 Apache 服务器并尝试以下代码

ob_start();
for($i = 0; $i < 10; $i ++) {
    echo $i;
    echo '<br />';
    flush();
    ob_flush();
    sleep(1);
}

Check your server api with

echo phpinfo();

If you found your server api

Server API :  CGI/FastCGI

in CentOS then add this line in "/etc/httpd/conf.d/fcgid.conf"

OutputBufferSize 0

To test, restart your Apache server and try below code

ob_start();
for($i = 0; $i < 10; $i ++) {
    echo $i;
    echo '<br />';
    flush();
    ob_flush();
    sleep(1);
}
深爱不及久伴 2024-10-19 10:12:20

我注意到浏览器的反应有所不同。例如,Chrome 会永远保留输入,并且似乎并不关心是否提前显示它。
不出所料,如果应用上述提示(由其他 stackoverflowers 提供),Firefox 会更早地显示输入,因此请尝试使用 Firefox。

I have noticed that browsers react differently. Chrome, for example, holds on to the input forever, and doesn't seem to care about displaying it any earlier.
Unsurprisingly, Firefox will display the input earlier, if the above tips (contributed by other stackoverflowers) are applied, so try with Firefox.

萌面超妹 2024-10-19 10:12:20

我只能通过这种方式刷新 - 添加 session_write_close();

if (ob_get_level() == 0)
{
    if(!ob_start("ob_gzhandler"))ob_start();
}               
echo ('bla bla bla');

$ans=ob_get_contents();
ob_end_clean();

header('Connection: close');
header('Content-Length: '.strlen($ans));
header('Status: 200');

echo $ans;

session_write_close();
ob_flush();
flush();

I was able to flush only this way - adding session_write_close();

if (ob_get_level() == 0)
{
    if(!ob_start("ob_gzhandler"))ob_start();
}               
echo ('bla bla bla');

$ans=ob_get_contents();
ob_end_clean();

header('Connection: close');
header('Content-Length: '.strlen($ans));
header('Status: 200');

echo $ans;

session_write_close();
ob_flush();
flush();
2024-10-19 10:12:20
if(!ob_get_level()) ob_start();
echo json_encode(array('valid'=>true,'msg'=>'Flush occured.'));
$size = ob_get_length();
header("Content-Type: application/json");
// Set the content length of the response.
header("Content-Length: {$size}");
//Close the connection if you want to.
header("Connection: close");
// Flush all output.
ob_end_flush();
ob_flush();
flush();
// Close current session (if it exists).
if(session_id()) session_write_close();
if(!ob_get_level()) ob_start();
echo json_encode(array('valid'=>true,'msg'=>'Flush occured.'));
$size = ob_get_length();
header("Content-Type: application/json");
// Set the content length of the response.
header("Content-Length: {$size}");
//Close the connection if you want to.
header("Connection: close");
// Flush all output.
ob_end_flush();
ob_flush();
flush();
// Close current session (if it exists).
if(session_id()) session_write_close();
梦纸 2024-10-19 10:12:20

我在 nginx 中使用 php-fpm 时尝试过很多次。许多答案只是指示:

在 php.ini 中:

output_buffering = Off

zlib.output_compression = Off

在 nginx.conf 中:

gzip off;

proxy_buffering off;

但他们忘记了 nginx.conf 中非常重要的设置:

fastcgi_keep_conn on;

I have tried many times when using php-fpm with nginx. Many answers just instruct:

In php.ini:

output_buffering = Off

zlib.output_compression = Off

In nginx.conf:

gzip off;

proxy_buffering off;

BUT they forgot the very important setting in nginx.conf:

fastcgi_keep_conn on;
橘味果▽酱 2024-10-19 10:12:20

这是唯一对我有用的没有填充的解决方案。

<?php
set_time_limit(0); # remove the time limit for running the script
ob_end_clean();

#header('Cache-Control: no-cache'); # activate if chaching is an issue
#header('Pragma: no-cache'); # activate if chaching is an issue
header('Connection: close');
header('Transfer-Encoding: identity');

ob_implicit_flush(1);

for($i=0;$i<5;$i++){
    echo json_encode(array(
        'timestamp'=>time(),
    ));
    usleep(500000);
}
?>

它给出了以下curl 输出:

HTTP/1.1 200 OK
Date: Sat, 25 Nov 2023 11:54:24 GMT
Server: Apache/2.4.57 (Unix) PHP/7.4.33
X-Powered-By: PHP/7.4.33
Connection: close
Transfer-Encoding: identity, chunked
Content-Type: text/html; charset=UTF-8
 
18
{"timestamp":1700913264}
18
{"timestamp":1700913265}
18
{"timestamp":1700913265}
18
{"timestamp":1700913266}
18
{"timestamp":1700913266}
0

在浏览器中看起来像这样:

{"timestamp":1700913264}{"timestamp":1700913265}{"timestamp":1700913265}{"timestamp":1700913266}{"timestamp":1700913266}

This is the only solution that worked for me without the padding.

<?php
set_time_limit(0); # remove the time limit for running the script
ob_end_clean();

#header('Cache-Control: no-cache'); # activate if chaching is an issue
#header('Pragma: no-cache'); # activate if chaching is an issue
header('Connection: close');
header('Transfer-Encoding: identity');

ob_implicit_flush(1);

for($i=0;$i<5;$i++){
    echo json_encode(array(
        'timestamp'=>time(),
    ));
    usleep(500000);
}
?>

It gave the following output from curl:

HTTP/1.1 200 OK
Date: Sat, 25 Nov 2023 11:54:24 GMT
Server: Apache/2.4.57 (Unix) PHP/7.4.33
X-Powered-By: PHP/7.4.33
Connection: close
Transfer-Encoding: identity, chunked
Content-Type: text/html; charset=UTF-8
 
18
{"timestamp":1700913264}
18
{"timestamp":1700913265}
18
{"timestamp":1700913265}
18
{"timestamp":1700913266}
18
{"timestamp":1700913266}
0

It looks like this in the browser:

{"timestamp":1700913264}{"timestamp":1700913265}{"timestamp":1700913265}{"timestamp":1700913266}{"timestamp":1700913266}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文