首页 文章

PHP / JS进度条

提问于
浏览
7

我正在尝试创建一个页面,它将从复杂的数据库查询和php解析中生成结果集......但这主要是关键点......重点是这需要一两分钟才能完成,并且我希望显示一个进度条而不是一个通用的gif动画“loading ...”图片 .

故障将是......

  • 用户打开页面A.

  • 页面A请求来自页面B的数据(很可能是AJAX) .

  • 页面B处理数据库中的100000个条目并解析它们 .

  • 页面A显示了一个进度条,大致显示了该过程的进度

  • Page B返回结果集 .

  • 页面A显示结果集 .

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

我查看了EventSource / Server-Sent-Events,它显示了承诺,我不太确定如何使其正常工作,或者是否有更好的方法来实现它 .

我试过制作一个快速的小模型页面,只使用EventSource工作正常,但当我将其拆分为eventSource调用(监视会话变量以进行更改的页面)和ajax请求(实际数据发送/它会崩溃 .

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

用户页面:

<!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调用运行请求的页面B,并将进度保存到SESSION变量

  • Page A AJAX每2秒调用一次页面C,它只返回会话变量的值 . 当该循环达到100时终止

但是,这也不是很有效 . 似乎两个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');"/>
<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>

Debug: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

1 回答

  • 2

    在类似的情况下,我通常这样做:

    • 客户端向页面B发送AJAX请求. Important :成功后,客户端再次发送相同的请求 .

    • 在初始请求中,Page B说: OK, THERE ARE 54555 RECORDS. . 我使用此计数来启动进度条 .

    • 在每个下一个请求中,页面B返回一大块数据 . 客户端计算块的大小并更新进度条 . 它还在一个列表中收集块 .

    • 在上次请求时,当发送所有数据时,页面B说: THAT'S ALL 并且客户端呈现数据 .

    我想,你已经有了这个主意 .

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

相关问题