Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/461.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript PHP脚本作为表单操作:提供服务器端效果_Javascript_Php_Html_Server Sent Events - Fatal编程技术网

Javascript PHP脚本作为表单操作:提供服务器端效果

Javascript PHP脚本作为表单操作:提供服务器端效果,javascript,php,html,server-sent-events,Javascript,Php,Html,Server Sent Events,我有一个运行很长时间的PHP脚本(最多5分钟)。我想在每个步骤后通知用户(“任务1/10已完成”) 我知道,这是一个老问题,我试图通过服务器端事件来解决它 在HTML端,有一个以我的PHP脚本作为操作的表单: <form action="my_script.php"><input type="submit" value="Submit!"/></form> 在服务器端,PHP应该向调用的HTML页面发送一些消息(在执行任务时),并在所有任务完成后使用所有输出

我有一个运行很长时间的PHP脚本(最多5分钟)。我想在每个步骤后通知用户(“任务1/10已完成”)

我知道,这是一个老问题,我试图通过服务器端事件来解决它

在HTML端,有一个以我的PHP脚本作为操作的表单:

<form action="my_script.php"><input type="submit" value="Submit!"/></form>
在服务器端,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;
但这不起作用

经过一些测试,我认为有两个问题:

  • 浏览器无法处理标题“内容类型:文本/事件流”,并提供下载文件
  • 表单操作(
    )和JavaScript事件(
    var source=new EventSource(“my_script.php”);
    )似乎都打开了php脚本的一个自己的线程,因此服务器上运行着该脚本的两个实例

  • 如何解决这些问题?

    要在过去实现这一点,您的表单将直接提交到
    my_script.php
    ,这将引导浏览器从该URL加载内容
    my_script.php
    将首先回显足够多的HTML以供浏览器呈现整个页面,然后刷新其输出缓冲区以确保客户端接收到所有内容该页面数据的一部分将是一个JS全局函数——类似于
    update\u status
    ,它将接收状态事件并更新UI。然后,在同一个请求上,它将启动处理

    在处理过程中的不同时间点,脚本将回显如下内容:

    <script type="text/javascript">update_status('15%');</script>
    
    update_status('15%);
    
    并将其刷新到客户端。客户端将在这些脚本标记出现时运行它们,调用相关函数,并更新UI

    今天我们有。如果你的用户有一个功能强大的浏览器,这是目前为止最好的方法


    您也可以在
    setInterval
    上轮询服务器,但这需要将执行拆分为不同的进程(以免挂起浏览器或浪费web服务器工作进程),并在后端添加端点以公开每个进程的状态。

    我只需要使用标准ajax轮询即可。 在本例中,我只是从长时间运行的脚本中写入一个文本文件,然后直接在轮询中加载它

    您可能会写入数据库等:

    <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';
    
    
    $(函数(){
    var checkid='';
    $('#myform')。提交(函数(ev){
    ev.preventDefault();
    var fm=$(本);
    $.ajax({
    类型:“POST”,
    url:fm.attr('action'),
    数据:“,
    成功:功能(数据){
    clearInterval(检查ID);
    $('#result').html(数据);
    }
    });
    checkid=setInterval(函数(){
    $.get('progress.txt',函数(数据){
    $('#progress').html(数据);
    });
    },1000);
    })
    });
    //long.php
    
    对于($i=1;$i这样的问题,有很多解决方案或变通方法。这里肯定不是讨论哪一个是最好的。我的问题的目的只是:如何通过服务器端事件解决该问题

    受用户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”)


    这似乎可行,但并不是真正平滑美观的解决方案。

    如果这只是一个常规表单提交,它在处理过程中不会停止浏览器中的执行吗?为什么要发明自己的自行车?我想要sockets.io而不是sockets.io请确认如果客户端不支持javascript@adeneo我不明白你的意思重点。我的问题是:脚本执行得很好,但它只有在完全完成后才会打印文本。我希望用户在脚本仍在运行时看到消息。@user574632:如果客户端不支持JavaScript,用户单击“提交”,PHP脚本在几分钟后运行并显示其结果。没有显示任何状态消息。这已经起作用了。因此,这里没有问题。啊,好的。我看了一下,得出结论,服务器端事件是解决我的问题的更好的解决方案,因为我认为WebSocket对于这样一个小问题来说过于复杂。但是我会研究一下。我同意WebSocket有点广泛,但据我所知,它们比EventSource有更好的支持——特别是在IE中。EventSource的一个问题是,如果你使用基于进程的服务器(如Apache),它会吞噬web服务器的工作人员。即使在大型服务器上,打开几百个这样的连接也会使你的网站没有响应。
    <?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();
    
    ?>