PHP-使用Ajax刷新While Loop数据


PHP - Flushing While Loop Data with Ajax

使用PHP,我想进行一个while循环,读取一个大文件,并在请求时发送当前行号使用Ajax,我想获得当前行数并将其打印到页面上使用html按钮,我希望能够单击并激活或终止只运行一次并调用ajax方法的javascript线程

我已经尝试过了,但由于某种原因,除非我注释掉echo str_repeat(' ',1024*64);函数,否则不会打印任何内容,并且当注释掉它时,它会显示整个循环结果:

处理了1行。处理了2行。处理3行。处理4行。处理5行。处理6行。处理7行。处理8行。处理9行。处理10行。

在一行中,而不是在单独的行中显示它们,如:

1 row(s) processed.
2 row(s) processed.
3 row(s) processed.
4 row(s) processed.
5 row(s) processed.
6 row(s) processed.
7 row(s) processed.
8 row(s) processed.
9 row(s) processed.
10 row(s) processed.

此外,我不知道如何终止JavaScript线程。所以总共有2个问题:

 1. It's returning the entire While loop object at once instead of each time it loops.
 2. I'm not sure how to terminate the JQuery thread.

有什么想法吗?以下是到目前为止我的代码。

msgserv.php

<?php
//Initiate Line Count
$lineCount = 0;
// Set current filename
$file = "test.txt";
// Open the file for reading
$handle = fopen($file, "r");
//Change Execution Time to 8 Hours
ini_set('max_execution_time', 28800);
// Loop through the file until you reach the last line
while (!feof($handle)) {
    // Read a line
    $line = fgets($handle);
    // Increment the counter
    $lineCount++;
    // Javascript for updating the progress bar and information
    echo $lineCount . " row(s) processed.";
    // This is for the buffer achieve the minimum size in order to flush data
    //echo str_repeat(' ',1024*64);
    // Send output to browser immediately
    flush();
    // Sleep one second so we can see the delay
    //usleep(100);
}
// Release the file for access
fclose($handle);
?>

asd.html

<html>
    <head>
        <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript" charset="utf-8"></script>
        <style type="text/css" media="screen">
            .msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
            .new{ background-color:#3B9957;}
            .error{ background-color:#992E36;}
        </style>
    </head>
    <body>
    <center>
        <fieldset>
            <legend>Count lines in a file</legend>
            <input type="button" value="Start Counting" id="startCounting" />
            <input type="button" value="Stop Counting!" onclick="clearInterval(not-Sure-How-To-Reference-Jquery-Thread);" />
        </fieldset>
    </center>
    <div id="messages">
        <div class="msg old"></div>
    </div>
    <script type="text/javascript" charset="utf-8">
        function addmsg(type, msg){
            /* Simple helper to add a div.
        type is the name of a CSS class (old/new/error).
        msg is the contents of the div */
            $("#messages").append(
            "<div class='msg "+ type +"'>"+ msg +"</div>"
        );
        }
        function waitForMsg(){
            /* This requests the url "msgsrv.php"
        When it complete (or errors)*/
            $.ajax({
                type: "GET",
                url: "msgsrv.php",
                async: true, /* If set to non-async, browser shows page as "Loading.."*/
                cache: false,
                timeout:2880000, /* Timeout in ms set to 8 hours */
                success: function(data){ /* called when request to barge.php completes */
                    addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
                    setTimeout(
                    'waitForMsg()', /* Request next message */
                    1000 /* ..after 1 seconds */
                );
                },
                error: function(XMLHttpRequest, textStatus, errorThrown){
                    addmsg("error", textStatus + " (" + errorThrown + ")");
                    setTimeout(
                    'waitForMsg()', /* Try again after.. */
                    "15000"); /* milliseconds (15seconds) */
                },
            });
        };
        $('#startCounting').click(function() {
            waitForMsg();
        });
    </script>
</body>
</html>

test.txt

1
2
3
4
5
6
7
8
9
10

您对PHP和AJAX的交互方式感到困惑。

当您通过AJAX请求PHP页面时,您会强制PHP脚本开始执行。尽管您可能使用flush()来清除任何内部PHP缓冲区,但AJAX调用在连接关闭之前不会终止(即,不会调用响应处理程序),这发生在读取整个文件时。

为了完成你想要的,我相信你需要一个类似这样的并行流程:

  1. 第一个AJAX帖子发送了一个开始读取文件的请求。这个脚本生成一些unqiue ID,将其发送回浏览器,生成一个实际执行文件读取的线程,然后终止
  2. 所有后续的AJAX请求都转到另一个PHP脚本,该脚本检查文件读取的状态。这个新的PHP脚本根据#1中生成的唯一ID发送文件读取的当前状态,然后退出

您可以通过$_SESSION变量或将数据存储到数据库中来实现这种进程间通信。无论哪种方式,您都需要一个并行实现,而不是当前的顺序实现,否则您将继续一次获得整个状态。

使用:

  • jQuery终止ajax请求
  • ignore_user_abort()
  • ob_flush()

应该在一个php线程中完成所有需要的操作

编辑

看看尼克的答案,如果你正在寻找一种简单的方法,那就是下面的算法:

  1. javascript通过ajax打开process.php(它将完成所有工作并打印状态报告),您必须查看jQuery ajax是否支持连续加载
  2. 如果用户决定停止刷新,您将终止加载,如提供的链接所示

process.php:中

ignore_user_abort(); // Script will finish in background
while(...){
  echo "Page: $i'n";
  ob_flush();
}

EDIT 2请求的示例(有点不同和丑陋,但很简单)test_process.php:

// This script will write numbers from 1 to 100 into file (whatever happens)
// And sends continuously info to user
$fp = fopen( '/tmp/output.txt', 'w') or die('Failed to open');
set_time_limit( 120);
ignore_user_abort(true);
for( $i = 0; $i < 100; $i++){
    echo "<script type='"text/javascript'">parent.document.getElementById( 'foo').innerHTML += 'Line $i<br />';</script>";
    echo str_repeat( ' ', 2048);
    flush();
    ob_flush();
    sleep(1);
    fwrite( $fp, "$i'n");
}
fclose( $fp);

和主html页面:

<iframe id="loadarea"></iframe><br />
<script>
function helper() {
    document.getElementById('loadarea').src = 'test_process.php';
}
function kill() {
    document.getElementById('loadarea').src = '';
}
</script>
<input type="button" onclick="helper()" value="Start">
<input type="button" onclick="kill()" value="Stop">
<div id="foo"></div>

击中起始线后为:

Line 1
Line 2

出现在div #foo中。当我点击Stop时,它们停止出现,但脚本在后台完成,并将所有100个数字写入文件。

如果您再次点击Start,脚本将从乞求(重写文件)开始执行,那么并行请求也会执行。

有关http流的更多信息,请参阅此链接

更简单的解决方案应该使用本机(vanila-js)XHR对象。

有非常复杂的解决方案,关于长轮询

PHP:

<?php
header('Content-Type: text/html; charset=UTF-8');
if (ob_get_level() == 0) ob_start();
for ($i = 0; $i<10; $i++){
  echo "<br> Line to show.";
  echo str_pad('',4096)."'n";
  ob_flush();
  flush();
  sleep(2);
}
echo "Done.";
ob_end_flush();

JS:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/some_service.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
  if (xhr.status == 200) {
    if (xhr.readyState == XMLHttpRequest.LOADING){
      console.log('response',xhr.response);
      // this can be also binary or your own content type 
      // (Blob and other stuff)
    }
    if (xhr.readyState == XMLHttpRequest.DONE){
      console.log('response',xhr.response);
    }
  }
}

诀窍是创建一个文件(通过Ajax更新)并使用setInterval获取其值,然后更新进度条。