我有一个运行很长时间的PHP脚本(最多5分钟)。我想在每个步骤后通知用户(“完成10个任务1”)。
我知道,这是一个老问题,我试图用服务器端事件解决它。
在HTML端,有一个表单,我的PHP脚本作为操作:
<form action="my_script.php"><input type="submit" value="Submit!"/></form>
一个要求是,孔HTML页面工作(并通过提交表单调用my_script.php)而不需要任何脚本。 PHP脚本的“实时状态报告”旨在成为一个附加功能,但页面应该完全没有它。
我尝试这样做:在客户端,我向调用JavaScript函数的onsubmit()
标记添加了form
个事件:
var source = new EventSource("my_script.php");
source.onmessage = function(event)
{
// display data in a div
};
在服务器端,PHP应该向调用HTML页面发送一些消息(在执行其任务时),并且在完成所有任务之后,构建一个包含所有输出的新HTML页面。 我试着这样做:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$output_stream = '';
// do something here. If a taks is done call send_to_output('something done');
function send_to_output($message)
{
global $output_stream;
echo "data: {$message}\n\n";
flush();
$output_stream .= $message;
}
header('Content-Type: text/html');
header('Cache-Control: no-cache');
echo "Everything is finished";
echo $output_stream;
但这不起作用。
经过一些测试,我认为有两个问题:
<form action="my_script.php">
)和JavaScript事件(var source = new EventSource("my_script.php");
)似乎各自打开PHP脚本的自己的线程,因此有两个实例这个脚本在服务器上运行。我该如何解决这些问题?
答案 0 :(得分:0)
为了在以前完成此操作,您的表单将直接提交到my_script.php
,这将指示浏览器从该URL加载内容。 my_script.php
首先会回显足够的HTML以供浏览器呈现整个页面,然后刷新其输出缓冲区以确保客户端收到所有内容。 该页面数据的一部分将是JS全局函数 - 类似于update_status
,它将接收状态事件并更新UI。然后,在同一请求中,它将开始处理。
在处理过程中的各个阶段,脚本会回显如下内容:
<script type="text/javascript">update_status('15%');</script>
并将其刷新到客户端。客户端将在进入时运行这些脚本标记,调用相关函数并更新UI。
今天我们有WebSockets。这是迄今为止最好的方法,为您的用户提供功能强大的浏览器。
您还可以在setInterval
上轮询服务器,但这需要将执行拆分为不同的进程(以免挂起浏览器或浪费Web服务器工作者)并将端点添加到后端揭露每个过程的状态。
答案 1 :(得分:0)
我会用标准的ajax轮询来做这件事。 在这个例子中,我只是从长时间运行的脚本写入文本文件,并直接在轮询中加载它。
你可能会写一个db等:
<html><head></head>
<body>
<form action="long.php" method="post" id="myform">
<input type="submit" value="submit "/>
</form>
<div id="progress">
</div>
<div id="result">
</div>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script type="application/javascript">
$(function(){
var checkid ='';
$('#myform').submit(function(ev){
ev.preventDefault();
var fm = $(this);
$.ajax({
type: "POST",
url: fm.attr('action'),
data: "",
success: function(data){
clearInterval(checkid);
$('#result').html(data);
}
});
checkid = setInterval(function(){
$.get('progress.txt', function(data){
$('#progress').html(data);
});
},1000);
})
});
</script>
</body>
</html>
//long.php
for($i=1;$i<10;$i++){
sleep(2);
file_put_contents('progress.txt', "done {$i} iterations", LOCK_EX);
}
echo 'all done';
答案 2 :(得分:0)
对于像这样的问题,有很多解决方案或解决方法。这肯定不是讨论哪个是最好的一个的地方。我的问题的意图只是:如何解决服务器端事件的问题?
受用户574632的启发我现在发现了一个快速而肮脏的解决方案:
我长时间运行的PHP脚本将所有消息放入文件中:
function send_to_output($message)
{
global $output_stream;
$output_stream .= $message;
file_put_contents('progress.txt',$output_stream);
}
另一个PHP脚本将此文件的所有内容发送到客户端:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$file_content = file_get_contents('progress.txt');
echo "data: {$file_content}\n\n";
flush();
?>
在HTML中,我只需要更改为var source = new EventSource("name.php")
的文件名。
这似乎有效,但它并不是真正流畅且美观的解决方案。