首页 > 请问PHP脚本执行时如何实时提供反馈

请问PHP脚本执行时如何实时提供反馈

有两个页面一个上传EXCEL文件,另一个接受这个EXCEL文件并处理,下面是这个页面处理时显示的内容,可是每次都要等程序完全运行才会显示。有时候时间太长没有反馈,用户会关闭页面,我想增加实时提示。求大家指点下

我想过用异步的方法,可第二个文件是在Ifram框架内的,异步获取也是要等待值的,最终显示还是一样。

//117条数据读取插入并且生成报表
11:04:17 开始读取EXCEL表。
11:04:31 读取并插入数据库成功
11:04:31 完成工作簿添加一个工作表。
11:04:32 完成工作表内容写入。
11:04:32 完成工作表克隆。
11:04:32 完成克隆工作表【失业】添加。
11:04:33 完成工作表克隆。
11:04:33 完成克隆工作表【医疗】添加。
11:04:33 完成工作表克隆。
11:04:33 完成克隆工作表【工伤】添加。
11:04:34 完成工作表克隆。
11:04:34 完成克隆工作表【生育】添加。
11:04:34 完成工作表克隆。
11:04:34 完成克隆工作表【重大疾病】添加。
11:04:35 完成工作表克隆。
11:04:35 完成克隆工作表【补充医疗】添加。
11:04:35 设置文件基本信息。
11:04:35 查询结果集数据库成功。
11:04:35 查询结果集转化为数组成功。
11:04:35 完成工作簿[养老]数据填入。
11:04:36 完成工作簿[失业]数据填入。
11:04:36 完成工作簿[医疗]数据填入。
11:04:36 完成工作簿[工伤]数据填入。
11:04:37 完成工作簿[生育]数据填入。
11:04:37 完成工作簿[重大疾病]数据填入。
11:04:38 完成工作簿[补充医疗]数据填入。
11:04:46 完成工作簿文件创立。

我专门的写了个解决方案的文章,去看看:http://.com/a/1190000004235213


题主问的应该是实时输出的问题,一般情况下一个php脚本在全部执行完毕后,才会将脚本输出一次性返回,而在执行过程中的输出内容是没有办法实时获取的,这种行为叫输出缓存,可以通过output.buffering(PHP手册)来开关

<?php
echo 1;
sleep(1);
echo 2;

访问这个页面,会在header内容全部输出1秒后,同时输出12

但在程序中可以通过flush强制将缓存内容输出并清空缓存

<?php
echo 1;
flush();
sleep(1);
echo 2;

加上flush之后,一般情况下1会在header返回后立即输出,1秒之后,再输出2

为什么是一般情况下呢,因为php的缓存是刷新了,但是如果前端web服务器(如apache)开启了gzip/deflate/cache等需要等待脚本输出完毕才能执行的功能,会被再一次缓存

如果真的需要这样的实时输出功能,需要把整个response链路上所有的缓存功能全部关闭,其实是有些得不偿失的~

http://php.net/manual/zh/function.flush.php


将程序放在队列中处理,将执行日志放到数据库或者文本,然后通过ajax轮询获取和处理进度并显示


我能想到的有两种办法,都是用AJAX异步的:

  1. 将每一个步骤都分成一个函数然后URL传参执行对应函数,比如step=first对应运行first()函数,运行成功返回你的内容。前端这边用AJAX请求,如果成功返回则append内容到页面上然后再继续请求下一步这样一直递归下去直至任务完成。可以参考我给Typecho写的在线升级插件,使用的就是这种方法。

  2. 这种方法就比较简单了PHP每次运行完一步就把内容append到一个JSON文件中,前端这边用AJAX轮询请求这个JSON文件并显示到页面上。


上传进度如 @宋小北 所说,鸟哥的博客中说的很明确:

1、PHP5.4之前,APC通过apc.rfc1867_freq string选项原生支持文件上传的缓存更新,参考 http://php.net/manual/zh/apc.configuration.php#ini.apc.rfc1867
2、PECL也有扩展实现上传进度 http://pecl.php.net/package/uploadprogress

如果你用较早版本,又不想搞上述劳什子,就用 Flash 上传组件如swfupload或者jQuery File Upload之类。

上传完成后实时显示处理结果,你可以关闭Web Server 和 PHP的各种buffer,然后靠flush()强制输出。但这是一个愚蠢的做法。
给一个简单思路,每处理一步就向第三方存储比如memcached,redis,甚至是文件里写入一个标记,通过ajax轮询这个标记,准实时更新dom里的内容。这应该是比较折衷比较理想的方案。恁看中不中?

====

@公子 老师指点了一下,我发现他的第一个方案把操作拆成几步来做更好,处理大文件时可以防止超时。同时要注意很多问题,比如文件状态维护防止漏操作,每一步都要重复读文件等等...我只是吃饭前来SG逗个乐子,具体的细节还是题主自己考虑吧。


非常感谢大家的回答,我用的是提交页面控制一个进度条,按下按钮进度条开始激活,设置一个大于用户完成操作的时间,当另一个页面处理完成时,计算出这个进度条未布满的部门,用额外5S 控制进度条速度填满


function flush_buffers()
{
    ob_end_flush();
    ob_flush();
    flush();
    ob_start('ob_callback');
}

function ob_callback($buffer)
{
    return $buffer . str_repeat(' ', max(0, 4097 - strlen($buffer)));
}

demo

echo '<br>处理1!<br>';
flush_buffers();

//do ....

echo '<br>处理2!<br>';
flush_buffers();

拓展:
PHP5.4上传进度支持(Upload progress in sessions)

【热门文章】
【热门文章】