【问题标题】:PHP: close output streamPHP:关闭输出流
【发布时间】:2012-01-23 20:13:28
【问题描述】:

是否可以关闭 PHP 脚本的输出流?我有一个脚本需要做一些后期处理,但是在后期处理期间和之后它不会再向客户端发送任何数据,所以我想在后期处理之前关闭连接。

编辑:在我的应用程序中,我有一个需要不时重建的缓存。但是,我不想让用户慢下来。我想要的是在脚本末尾确定是否需要重建缓存。所以我想先关闭输出流,让用户得到它的数据,然后我想重建缓存。这样做并不是很重要,但我认为最好先关闭连接,这样如果需要很长时间,用户就不会注意到正在重建缓存。

【问题讨论】:

  • 为什么需要这样做?你能发布一些你的代码来帮助我们理解更多吗?
  • +1 乔纳森所说的话。告诉我们更多关于你为什么要这样做的原因。

标签: php http-headers outputstream


【解决方案1】:

更新

处理这种情况的方法是结合输出缓冲和适当的 HTTP 标头。

来自HTTP/1.1 Specification Section 14.10:

HTTP/1.1 为发送者定义了“关闭”连接选项 表示连接将在完成后关闭 回应。

所以,如果我们在 Connection: close 之外传递一个 HTTP Content-Length 头,浏览器知道在收到指定的响应长度后关闭连接:

  1. 缓冲所有脚本输出,以便您保留发送标头的能力
  2. 获得完整的输出数据后,将适当的标头发送到客户端
  3. 继续您的处理...但不要尝试发送输出,否则您会收到错误,因为标头已发送。

另外,请小心,因为如果您执行过多的处理,您可能会遇到 Web 服务器 SAPI 中的脚本执行时间限制。最后,您应该使用ignore_user_abort() 告诉 PHP 忽略此特定脚本中的“用户中止”,因为浏览器会因为您正在执行的操作而关闭连接,并且您希望 PHP 继续处理。

<?php
ignore_user_abort();
ob_start();

// do stuff, generate output

// get size of the content
$length = ob_get_length();

// tell client to close the connection after $length bytes received
header('Connection: close');
header("Content-Length: $length");

// flush all output
ob_end_flush();
ob_flush();
flush();

// close session if you have one ...
// continue your processing tasks ...
?>

您可以查看Connection handlingdocs 上的 PHP 手册部分。

另外,为什么不开始输出缓冲呢?然后,您可以捕获将要发送的所有输出,然后再决定您是否真的想用它做任何事情。

<?php

echo 'before output buffering';
ob_start();
echo 'after output buffering';
$output = ob_get_contents();

// script's only output to this point will be 'before output buffering'

// I changed my mind, send the output ...
ob_end_flush();
?>

【讨论】:

  • 输出缓冲仍然保持浏览器的输出流打开。所以假设我需要做一些需要几秒钟左右的后期处理,浏览器会等到后期处理完成。尤其是使用 AJAX,这会大大降低 web 应用程序的速度。所以我希望能够关闭与浏览器的连接,这样浏览器就可以处理输出,然后继续进行后期处理。
  • @Tiddo 我已经用(我相信的)正确的解决方案更新了我的答案。
  • 这是一个非常好的解决方案!我马上试试。编辑:效果很好!非常感谢!
  • @Tiddo 还有一件事——我补充说你应该在脚本顶部调用ignore_user_abort()。这可能从我原始答案中的 PHP 手动链接中很明显,但我想明确说明。所以,只要确保你这样做:)
  • 好的,非常感谢!在我的脚本中,我还为发送原始输出后的所有内容添加了输出缓冲,因此如果意外发送了任何输出,我可以丢弃它并且脚本不会崩溃。
【解决方案2】:
【解决方案3】:

我没有足够的声誉来发表评论,但我想在@rdlowrey 回答中分享这一点gzip 可能是个问题。

如果您启用了 gzip,则 Transfer-encoding 标头始终设置为 chunked,即使您尝试使用 @987654321 更改它也是如此@ 因此它不会发送 Content-Length 标头。

我可以解决这个问题的方法是使用下一个:

<?
@ini_set('zlib.output_compression', 'Off');
@ini_set('output_buffering', 'Off');
@ini_set('output_handler', '');
@apache_setenv('no-gzip', 1);
?>

然后解决办法:

<?
ignore_user_abort();
ob_start();

// do stuff, generate output

// get size of the content
$length = ob_get_length();

// tell client to close the connection after $length bytes received
header('Connection: close');
header("Content-Length: $length");

// flush all output
ob_end_flush();
flush();

// close session if you have one ...
// continue your processing tasks ...
?>

【讨论】:

    猜你喜欢
    • 2015-06-18
    • 2012-02-07
    • 2017-03-26
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    相关资源
    最近更新 更多