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

时间:2012-08-24 06:36:53

标签: php http

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

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

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

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

5 个答案:

答案 0 :(得分:10)

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

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

ignore_user_abort()

这将允许您的脚本在客户端避开闪避之后继续运行。但我们也面临着如何告诉客户他们所做的请求已完成以便关闭连接的问题。通常,如果我们不指定它们,PHP会透明地处理为我们发送这些标头。但是,我们需要明确地执行此操作,否则客户端将不知道我们何时希望它们停止阅读响应。

为此,我们必须发送适当的HTTP标头以告知客户端何时关闭:

Connection: close
Content-Length: 42

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

  1. 您必须生成之前发送任何输出的响应,因为您必须确定其内容长度大小(以字节为单位),以便您可以发送正确的标头。
  2. 你必须实际发送这些标题 BEFORE 你回显任何输出。
  3. 所以你的脚本看起来像这样:

    <?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添加一个检查,以便在完成转移之前用户中止时可以避免其他处理。

答案 1 :(得分:2)

我在twitter和&amp ;;上传图片时遇到同样的问题来自iphone的facebook通过php的网络服务。

如果图片上传的处理时间不多,那么您可以查看@Musa的评论,这可能会对您有所帮助,但如果需要花费太多时间来处理,请尝试以下步骤。

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

这些会缩短您的处理时间。

希望这对你有所帮助。

答案 2 :(得分:1)

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

通过这种方式思考你正在做正确的事;继续进行一个小的建筑步骤,并将要求与需要进行的繁重工作完全分离。

答案 3 :(得分:1)

您可以通过多种方式实现#

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脚本关闭进程

答案 4 :(得分:1)

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

参考

How do i implement this scenario using PHP?

OR

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

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