【问题标题】:How to flush data to browser but continue executing如何将数据刷新到浏览器但继续执行
【发布时间】:2012-05-21 16:31:39
【问题描述】:

我有一个ob_start() 和一个对应的ob_flush()。我想刷新一部分数据并继续执行其余部分。使用ob_flush() 没有帮助。此外,如果可能需要休息而不显示浏览器中的加载。

编辑:

我不想使用 ajax

【问题讨论】:

  • 执行第一个 ob_flush 后,是否要显示其他输出?或者您是否希望客户端可以告诉您请求完全完成,然后在后台继续进行某种处理?
  • 如果您的输出缓冲已经在 php.ini 中关闭,您只需要 flush(),但是要使其工作,您需要配置 Apache、NginX 等。

标签: php output-buffering


【解决方案1】:

我过去曾这样做过,这就是我解决它的方法:

ob_start();

/*
 * Generate your output here
 */ 

// Ignore connection-closing by the client/user
ignore_user_abort(true);

// Set your timelimit to a length long enough for your script to run, 
// but not so long it will bog down your server in case multiple versions run 
// or this script get's in an endless loop.
if ( 
     !ini_get('safe_mode') 
     && strpos(ini_get('disable_functions'), 'set_time_limit') === FALSE 
){
    set_time_limit(60);
}

// Get your output and send it to the client
$content = ob_get_contents();         // Get the content of the output buffer
ob_end_clean();                      // Close current output buffer
$len = strlen($content);             // Get the length
header('Connection: close');         // Tell the client to close connection
header("Content-Length: $len");     // Close connection after $len characters
echo $content;                       // Output content
flush();                             // Force php-output-cache to flush to browser.
                                     // See caveats below.

// Optional: kill all other output buffering
while (ob_get_level() > 0) {
    ob_end_clean();
}

正如我之前在几个 cmets 中所说,您应该注意对内容进行 gzip 压缩,因为这会改变内容的长度,但不会更改有关内容的标题。它还可以缓冲您的输出,因此不会立即发送到客户端。
您可以尝试使用apache_setenv('no-gzip', '1'); 让 apache 知道不要压缩您的内容。但是,如果您使用重写规则转到您的页面,这将不起作用,因为它也会修改这些环境变量。至少,它对我来说是这样的。

manual 中查看有关将您的内容刷新给用户的更多注意事项。

【讨论】:

  • 仅供参考,PHP 函数是 ob_get_contentS
【解决方案2】:

ob_flush 写入缓冲区。换句话说,ob_flush 告诉 PHP 给 Apache(或 nginx/lighttpd/whatever)输出,然后让 PHP 忘记它。一旦 Apache 有了输出,它就可以随心所欲地使用它。 (换句话说,在ob_flush 之后,它是否会立即写入浏览器,这是您无法控制的)。

所以,简短的回答:没有保证可以做到这一点。

只是猜测,您可能正在寻找 AJAX。每当人们像您一样尝试在页面内容加载时进行操作时,AJAX 几乎总是正确的路径。

如果您想在后台继续执行任务,可以使用ignore_user_abort,详细说明为here,但是,这通常不是最佳方法。您基本上失去了对该线程的控制,在我看来,Web 服务器线程不属于繁重的处理。

我会尝试从面向网络的内容中提取它。这可能意味着一个 cron 条目或只是从 PHP 内部生成一个后台进程(虽然从脚本执行内部启动的进程不会随着脚本而死,并且脚本不会等待它完成才死)。

如果你真的走那条路,这意味着你甚至可以在必要时制作某种状态系统。然后,您可以监控执行情况并定期向用户提供进度更新。 (从技术上讲,您也可以使用 ignore_user_abort-ed 脚本创建状态系统,但它对我来说似乎不太干净。)

【讨论】:

  • 当新用户登录时,我想保存很多数据。此数据应立即保存但不会中断用户,即在浏览器上显示加载
【解决方案3】:

这是我的功能

function bg_process($fn, $arr) {
    $call = function($fn, $arr){
        header('Connection: close');
        header('Content-length: '.ob_get_length());
        ob_flush();
        flush();
        call_user_func_array($fn, $arr);
        };
    register_shutdown_function($call, $fn, $arr);
    }

将要执行的函数包装在最后,php关闭连接后。当然浏览器会停止缓冲。

function test() {
    while (true) {
        echo 'this text will never seen by user';
        }
    }

这是调用函数的方法

bg_process('test'); 

第一个参数是callable, 第二个参数是一个要传递给带有索引数组的“测试”函数的数组

注意:我不在脚本开头使用ob_start()

【讨论】:

    【解决方案4】:

    我有一篇文章解释了如何在我的博客上使用 apache/mod_php 来实现这一点:http://codehackit.blogspot.com/2011/07/how-to-kill-http-connection-and.html希望这会有所帮助,干杯

    【讨论】:

    • 如果您在此处添加一些关于其工作原理的 sn-p 将会很有帮助,因此如果您的网站出现故障,我们仍然可以在这里找到答案。您在博文中编写的代码有效。但是,如果您之前告诉 apache 对您的内容进行 gzip,它将不再起作用(因为长度不再正确)
    【解决方案5】:

    如果您使用的是 PHP-FPM:

    ignore_user_abort(true);
    fastcgi_finish_request();
    

    以上两个功能是ignore_user_abort防止错误和fastcgi_finish_request关闭客户端连接的关键因素。

    【讨论】:

      【解决方案6】:

      fastcgi_finish_request

      此函数将所有响应数据刷新到客户端并完成请求。这允许在不打开与客户端的连接的情况下执行耗时的任务。

      不适用于 Apache。(PHP 5 >= 5.3.3,PHP 7)

      【讨论】:

        【解决方案7】:

        用途:

        header("Content-Length: $len");
        

        ..其中$len 是要刷新到客户端的数据的长度。

        我不知道这将在何时何地起作用,但我尝试了几个浏览器,并立即返回:

        <?PHP 
            header("Content-length:5");
            echo "this is more than 5";
            sleep(5);
        ?>
        

        编辑:Chrome、IE 和 Opera 显示 this,而 FireFox 显示 this is more than 5。在那之后,他们所有人都关闭了请求。

        【讨论】:

        • 这就是为什么内容长度需要与实际内容长度相等的原因。请注意 gzip,因为它会修改您的响应长度。
        • @Zombaya,不过我不明白这有什么关系。只要他在一定数量后不试图切断他的输出,这应该可以在发送必要的数据后结束请求。
        • 好吧,当我尝试这样做并且长度与实际长度不匹配时,连接不会总是关闭并继续加载。
        • @Zombaya,为什么长度与实际长度不匹配?为什么他不能做header("Content-Length: ".strlen($content));
        • 这可以正常工作,但是如果您将 apache 配置为在输出的网页上使用 gzip 压缩,它会修改您的响应内容,但不会修改 content-length-header。过去我遇到了一些麻烦。
        猜你喜欢
        • 1970-01-01
        • 2015-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多