PHP - 使用 Ajax 刷新 While 循环数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9152373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP - Flushing While Loop Data with Ajax
提问by ThreaT
Using PHP, I would like to make a while loop that reads a large file and sends the current line number when requested. Using Ajax, I'd like to get the current line count and print it out onto a page. Using html buttons, I'd like to be able to click and activate or terminate a javascript thread that runs only ONCE and calls the ajax method.
使用 PHP,我想创建一个 while 循环来读取一个大文件并在请求时发送当前行号。使用 Ajax,我想获取当前行数并将其打印到页面上。使用 html 按钮,我希望能够单击并激活或终止仅运行一次并调用ajax 方法的 javascript 线程。
I have given it a shot but for some reason, nothing prints unless I comment out the echo str_repeat(' ',1024*64);
function and when it's commented out, it shows the entire loop result:
我已经试过了,但出于某种原因,除非我注释掉该echo str_repeat(' ',1024*64);
函数,否则不会打印任何内容,并且当它被注释掉时,它会显示整个循环结果:
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.
已处理 1 行。已处理 2 行。已处理 3 行。已处理 4 行。已处理 5 行。已处理 6 行。已处理 7 行。8已处理行。已处理 9 行。已处理 10 行。
In a single line instead of showing them in separate lines like:
在一行中而不是在单独的行中显示它们,例如:
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.
Also I'm not sure how to terminate the JavaScript thread. So 2 problems in total:
另外我不确定如何终止 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.
Any ideas? Below is my code so far.
有任何想法吗?以下是我到目前为止的代码。
msgserv.php
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
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
测试.txt
1
2
3
4
5
6
7
8
9
10
采纳答案by Vyktor
Using:
使用:
should do all you need in one php thread
应该在一个 php 线程中完成您需要的一切
EDIT
编辑
Take a look at nickb's answer, if you're looking for a way how to do this simply it would be following algorithm:
看看nickb的答案,如果您正在寻找一种方法来简单地做到这一点,它将遵循算法:
- javascript opens
process.php
via ajax (which will do all the work AND print status reports), you have to look up whether jQuery ajax supports continuous loading - if user decides to stop refreshes you'll kill loading as show in provided link
- javascript
process.php
通过ajax打开(它将完成所有工作并打印状态报告),您必须查找jQuery ajax是否支持连续加载 - 如果用户决定停止刷新,您将终止加载,如提供的链接所示
In process.php
:
在process.php
:
ignore_user_abort(); // Script will finish in background
while(...){
echo "Page: $i\n";
ob_flush();
}
EDIT 2requested example (bit of different and ugly, but simple). test_process.php
:
编辑 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);
And main html page:
和主 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>
After hitting start lines as:
在点击开始行后:
Line 1
Line 2
Appeared in the div #foo
. When I hit Stop
, they stopped appearing but script finished in background and written all 100 numbers into file.
出现在 div 中#foo
。当我点击 时Stop
,它们停止出现,但脚本在后台完成并将所有 100 个数字写入文件。
If you hit Start
again script starts to execute from the begging (rewrite file) so would parallel request do.
如果您Start
再次点击脚本从请求(重写文件)开始执行,那么并行请求也会这样做。
For more info on http streaming see this link
有关 http 流的更多信息,请参阅此链接
回答by nickb
You're confused as to how PHP and AJAX interact.
您对 PHP 和 AJAX 如何交互感到困惑。
When you request the PHP page via AJAX, you force the PHP script to begin execution. Although you might be using flush()
to clear any internal PHP buffers, the AJAX call won't terminate (i.e., the response handlers won't be called) until the connection is closed, which occurs when the entire file has been read.
当您通过 AJAX 请求 PHP 页面时,您会强制 PHP 脚本开始执行。尽管您可能正在使用flush()
清除任何内部 PHP 缓冲区,但在连接关闭之前,AJAX 调用不会终止(即,不会调用响应处理程序),这会在读取整个文件时发生。
To accomplish what you're looking for, I believe you'd need a parallel process flow like this:
为了完成你正在寻找的东西,我相信你需要一个像这样的并行流程:
- The first AJAX post sends a request to begin reading the file. This script generates some unqiue ID, sends that back to the browser, spawns a thread that actually does the file reading, then terminates.
- All subsequent AJAX requests go to a different PHP script that checks the status of the file reading. This new PHP script sends the current status of the file reading, based on the unique ID generated in #1, then exits.
- 第一个 AJAX 帖子发送一个开始读取文件的请求。该脚本生成一些独特的 ID,将其发送回浏览器,生成一个实际读取文件的线程,然后终止。
- 所有后续的 AJAX 请求都转到不同的 PHP 脚本,该脚本检查文件读取的状态。这个新的 PHP 脚本根据 #1 中生成的唯一 ID 发送文件读取的当前状态,然后退出。
You could accomplish this inter-process communication through $_SESSION
variables, or by storing data into a database. Either way, you need a parallel implementation instead of your current sequential one, otherwise you will continue to get the entire status at once.
您可以通过$_SESSION
变量或通过将数据存储到数据库中来完成这种进程间通信。无论哪种方式,您都需要并行实现而不是当前的顺序实现,否则您将继续立即获得整个状态。
回答by JonnieJS
Simpler solution should be using the native (vanila js) XHRobject.
更简单的解决方案应该是使用原生(vanila js)XHR对象。
There is very sophisticated solutions out there, about long polling
有非常复杂的解决方案,关于长轮询
The PHP:
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();
The JS:
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);
}
}
}
回答by Didier Sampaolo
The trick is to create a file (updated via Ajax) and use a setInterval
to get its value, then update the progressbar.
诀窍是创建一个文件(通过 Ajax 更新)并使用 asetInterval
获取其值,然后更新进度条。