PHP/JS 进度条

PHP/JS Progress Bar

我正在尝试制作一个页面,该页面将从复杂的数据库查询和 php 解析中生成结果集...但这主要是题外话...重点是这个需要一两分钟才能完成,我希望显示进度条而不是通用 gif 动画 "loading..." 图片。

细目是...

我知道如何 return 数据到 ajax 查询,但我的问题是我不知道如何连续 return 数据来显示进程状态(例如,扫描的行数百分比)。

我研究了 EventSource / Server-Sent-Events,这显示了希望,我只是不太确定如何让它正常工作,或者是否有更好的方法来做到这一点。

我试过制作一个快速的小模型页面,只使用 EventSource 工作正常,但是当我将它分成一个 eventSource 调用(页面监视会话变量的变化)和一个 ajax请求(实际数据sending/return)它崩溃了。

我可能遗漏了一些明显的东西,或者做了一些愚蠢的错误,但这就是我所拥有的大部分...任何帮助、建议、提示,甚至是完全其他方法的建议都是太棒了:)

用户页面:

<!DOCTYPE html>
<html>
<head>
    <title>Dynamic Progress Bar Example</title>
    <script src="script.js"></script>
</head>
<body>
    <input type="button" value="Submit" onclick="connect()" />
    <progress id='progressor' value="0" max='100' style=""></progress>
</body>
</html>

Javascript

 var es;

   function connect() {
       startListener();
       $.ajax({
           url: "server.php",
           success: function() {
               alert("Success");
           },
           error: function() {
               alert("Error");
           }
       });
   }

   function startListener() {
       es = new EventSource('monitor.php');

       //a message is received
       es.addEventListener('message', function(e) {
           var result = JSON.parse(e.data);

           if (e.lastEventId == 'CLOSE') {
               alert("Finished!");
               es.close();
           } else {
               var pBar = document.getElementById('progressor');
               pBar.value = result;
           }
       });

       es.addEventListener('error', function(e) {
           alert('Error occurred');
           es.close();
       });
   }

   function stopListener() {
       es.close();
       alert('Interrupted');
   }

   function addLog(message) {
       var r = document.getElementById('results');
       r.innerHTML += message + '<br>';
       r.scrollTop = r.scrollHeight;
   }

监控PHP

<?php
SESSION_START();
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache'); 

function send_message($id, $data) {
    $d = $data;
    if (!is_array($d)){
        $d = array($d);
    }

    echo "id: $id" . PHP_EOL;
    echo "data: " . json_encode($d) . PHP_EOL;
    echo PHP_EOL;

    ob_flush();
    flush();
}


$run = true;
$time = time();
$last = -10;

while($run){
    // Timeout kill checks
    if (time()-$time > 360){
        file_put_contents("test.txt", "DEBUG: Timeout Kill", FILE_APPEND);
        $run = false;
    }

    // Only update if it's changed
    if ($last != $_SESSION['progress']['percent']){
        file_put_contents("test.txt", "DEBUG: Changed", FILE_APPEND);
        $p = $_SESSION['progress']['percent'];
        send_message(1, $p); 
        $last = $p;
    }

    sleep(2);
}
?>

编辑: 我尝试了一种不同的方法,其中:

但是,这也不是很有效。看来这两个AJAX请求,或者服务器端的两个脚本不是同时运行。

查看调试输出:两个 AJAX 调用几乎同时执行,但随后页面 B 脚本自行运行完成,然后 - 然后 - 页面 C 脚本运行。这是 PHP 我缺少的一些限制吗???

更多代码!

服务器(页面B)PHP

<?PHP
    SESSION_START();

    file_put_contents("log.log", "Job Started\n", FILE_APPEND);

    $job = isset($_POST['job']) ? $_POST['job'] : 'err_unknown';
    $_SESSION['progress']['job'] = $job;
    $_SESSION['progress']['percent'] = 0;

    $max = 10;
    for ($i=0; $i<=$max;$i++){
        $_SESSION['progress']['percent'] = floor(($i/$max)*100);
        file_put_contents("log.log", "Progress now at " . floor(($i/$max)*100) . "\n", FILE_APPEND);
        sleep(2);
    }

    file_put_contents("log.log", "Job Finished", FILE_APPEND);
    echo json_encode("Success. We are done.");
?>

进度(C页)PHP

<?php
    SESSION_START();
    file_put_contents("log.log", "PR: Request Made", FILE_APPEND);

    if (isset($_SESSION['progress'])){
        echo json_encode(array("job"=>$_SESSION['progress']['job'],"progress"=>$_SESSION['progress']['percent']));
    } else {
        echo json_encode(array("job"=>"","progress"=>"error"));
    }
?>

索引(A页)JS/HTML

<!DOCTYPE html>
<html>
<head>
        <title>Progress Bar Test</title>
</head>
<body>
        <input type="button" value="Start Process" onclick="start('test', 'pg');"/><br />
        <progress id="pg" max="100" value="0"/>

        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <script type="text/javascript">
            var progress = 0;
            var job = "";

            function start(jobName, barName){
                startProgress(jobName, barName);
                getData(jobName);
            }

            function getData(jobName){
                console.log("Process Started");
                $.ajax({
                    url: "server.php",
                    data: {job: jobName},
                    method: "POST",
                    cache: false,
                    dataType: "JSON",
                    timeout: 300,
                    success: function(data){
                        console.log("SUCCESS: " + data)
                        alert(data);
                    },
                    error: function(xhr,status,err){
                        console.log("ERROR: " + err);
                        alert("ERROR");
                    }
                });
            }

            function startProgress(jobName, barName){
                console.log("PG Process Started");
                progressLoop(jobName, barName);
            }

            function progressLoop(jobName, barName){
                console.log("Progress Called");
                $.ajax({
                    url: "progress.php",
                    cache: false,
                    dataType: "JSON",
                    success: function(data){
                        console.log("pSUCCESS: " . data);
                        document.getElementById(barName).value = data.progress;
                        if (data.progress < 100 && !isNaN(data.progress)){
                            setTimeout(progressLoop(jobName, barName), (1000*2));
                        }
                    },
                    error: function(xhr,status,err){
                        console.log("pERROR: " + err);
                        alert("PROGRESS ERROR");
                    }
                });
            }
        </script>
</body>
</html>

调试:log.log输出

PR: Request Made
Job Started
Progress now at 0
Progress now at 10
Progress now at 20
Progress now at 30
Progress now at 40
Progress now at 50
Progress now at 60
Progress now at 70
Progress now at 80
Progress now at 90
Progress now at 100
Job Finished
PR: Request Made

类似的情况,我一般是这样处理的:

  • 客户端向页面 B 发送 AJAX 请求。 重要:成功后,客户端再次发送相同的请求。
  • 在最初的请求中,页面 B 说:OK, THERE ARE 54555 RECORDS.。我用这个计数来启动进度条。
  • 在接下来的每个请求中,页面 B return 都是一大块数据。客户端计算块的大小并更新进度条。它还在一个列表中收集块。
  • 在最后一个请求中,发送完所有数据后,页面 B 说:THAT'S ALL 并且客户端呈现数据。

我想,你已经明白了。

注意:您可以并行请求所有块,但这是一种复杂的方式。服务器(页面 B)也应该 return 初始响应中的固定块大小,然后客户端并发发送 TOTAL_COUNT / CHUNK_SIZE 请求并合并响应,直到最后一个请求完成。所以它要快得多。在这种情况下,您可以使用 https://github.com/caolan/async 来使代码更具可读性。