如何在服务器中运行PHP代码的同时阻止PHP向客户端发送数据


How do I stop PHP from sending data to client while still running PHP code in server?

当我遇到一个导致PHP程序无限循环的错误时,我想到了这个问题。以下是一个示例情况:

假设我有一个接收图片上传的PHP网页(该页面可能是图像上传表单的响应页面)。在服务器中,脚本应该将图像存储在一个临时文件中。然后脚本应该向客户端输出一条确认消息,然后停止发送数据,这样客户端就不会等待了。然后脚本应该继续执行,在结束之前处理图像(比如调整图像大小)。

我认为这种"技术"可能很有用,这样客户就不会在耗时的过程中等待,从而防止超时。

另外,使用HTTP方法可以解决这个问题吗?

如果您正确地使用HTTP头,那么这可以在没有任何异步处理的情况下轻松完成。

在正常情况下,PHP会在另一端的客户端关闭连接后立即停止处理。如果您想在这个事件之后继续处理,您需要做一件事:告诉PHP忽略用户中止。怎样

ignore_user_abort()

这将允许您的脚本即使在客户端摆脱躲避后也能继续运行。但我们也面临着一个问题,即如何告诉客户他们提出的请求已经完成,以便关闭连接。通常,如果我们不指定这些头,PHP会透明地为我们处理发送这些头。不过,在这里,我们需要明确地这样做,否则客户端将不知道我们希望他们何时停止读取响应。

要做到这一点,我们必须发送适当的HTTP头来告诉客户端何时关闭:

Connection: close
Content-Length: 42

这个头的组合告诉客户端,一旦它读取了42字节的实体主体响应,消息就完成了,它们应该关闭连接。这种方法有几个后果:

  1. 您必须在发送任何输出之前生成响应,因为您必须确定其以字节为单位的内容长度大小,以便发送正确的标头
  2. 您必须在回显任何输出之前发送这些标头

所以你的脚本可能看起来像这样:

<?php
ignore_user_abort();
// do work to determine the response you want to send ($responseBody)
$contentLength = strlen($responseBody);
header('Connection: close');
header("Content-Length: $contentLength");
flush();
echo $responseBody;
// --- client will now disconnect and you can continue processing here ---

这种方法的最大"Gotchya"是,当你在web SAPI中运行PHP时,如果你在最终用户客户端关闭连接后进行耗时的处理,你可以很容易地遇到最长时间限制指令。如果这是一个问题,您可能需要考虑使用cron的异步处理选项,因为PHP在CLI环境中运行时没有时间限制。或者,您可以使用set_time_limitdocs在web环境中增加脚本的时间限制。

值得一提的是,如果你这样做,你可能还想在生成响应体时向connection_aborted()docs添加一个检查,这样,如果用户在完成传输之前中止,你就可以避免额外的处理。

当我在twitter上上传图像时,我也遇到了同样的问题;facebook从iphone通过php的web服务。

如果图像上传的处理时间不多,那么你可以查看@Musa的评论,这可能会对你有所帮助,但如果处理时间太长,那么试试这些步骤。

 1. Image store in folder
 2. Fetch image from folder using cron 
 3. Cron run for every 2 min in backend

这将减少您的处理时间。

希望这对你有所帮助。

建议异步执行这些操作。也就是说,制作另一个只处理先前创建的tmp文件的脚本,并使用cron运行它(甚至不涉及apache)。当php作为web服务器模块运行时,它应该专门用于快速形成响应,然后为下一个请求释放资源。

你这样想是在做正确的事情;只需在体系结构上继续前进一小步,并将请求与需要进行的繁重工作完全解耦。

你可以用几种方法#

1#

ob_start();
//output
header("Content-Length: ".ob_get_length());
header("Connection: close");
ob_end_flush();
//do other stuff

2#

使用PHP的system()或exec()关闭Process

3#

使用Shell Script 关闭进程

您可以使用ob_implicit_flush(),它将打开或关闭隐式刷新。隐式刷新将在每次输出调用后产生刷新操作,因此不再需要显式调用flush()。

参考

如何使用PHP实现此场景?

您应该创建一个独立的cron,它将在特定的时间后运行,并以异步的方式执行,不让用户知道正在进行的处理,也不让用户等待。这样,您甚至可以检测到失败的案例。

您还应该尽量减少加载时间。