我有一個PHP可能需要10或甚至更多的秒。我想爲用戶顯示它的進度。顯示長時間運行PHP腳本的進度
在正在執行的類中,我有一個屬性$progress
,它隨着進度(1-100)進行更新,方法get_progress()
(其目的應該是顯而易見的)。
問題是,如何更新<progress>
元素在前端供用戶看?
我認爲AJAX是解決方案,但我無法繞開它。 (我無法到達同一個對象實例)。在此先感謝
我有一個PHP可能需要10或甚至更多的秒。我想爲用戶顯示它的進度。顯示長時間運行PHP腳本的進度
在正在執行的類中,我有一個屬性$progress
,它隨着進度(1-100)進行更新,方法get_progress()
(其目的應該是顯而易見的)。
問題是,如何更新<progress>
元素在前端供用戶看?
我認爲AJAX是解決方案,但我無法繞開它。 (我無法到達同一個對象實例)。在此先感謝
如果你的任務是上傳一個巨大的數據集或處理在服務器上,同時更新進度的服務器,你應該考慮用一些去作業體系結構,在那裏你開始工作,並在服務器上運行一些其他腳本(例如縮放/處理圖像等)。在這個過程中,你一次只做一件事,從而形成一個有輸入和最終處理輸出的任務流水線。
在流水線的每個步驟中,任務的狀態在數據庫內更新,然後可以通過現在存在的任何服務器推送機制發送給用戶。運行處理上傳和更新的單個腳本會在您的服務器上加載負載,並限制您(如果瀏覽器關閉,如果發生其他錯誤會怎麼樣)。當進程分成多個步驟時,您可以從最後一個成功的地方恢復失敗的任務。
有很多方法可以做到這一點。但整體流程是這樣的
下面的方法是什麼我做了一個個人項目,該腳本舉辦好上傳和到我的服務器處理成千上萬的高分辨率圖像,然後按比例降低分成多個版本並上傳到亞馬遜S3,同時識別其中的物體。 (我原來的代碼是在Python)
啓動先上傳內容,然後立即返回事務ID或UUID對本次交易通過一個簡單的POST請求運輸或任務
。如果你正在做的任務,多個文件或多個的東西,你可能還需要在處理這一步
做的工作&返回進度。
一旦你弄清楚事務是如何發生的,你可以使用任何服務器端推送技術來發送更新數據包。我會選擇WebSocket或服務器發送事件,無論哪種情況適用於不支持的瀏覽器上的長輪詢。一個簡單的SSE方法看起來像這樣。
function TrackProgress(upload_id){
var progress = document.getElementById(upload_id);
var source = new EventSource('/status/task/' + upload_id);
source.onmessage = function (event) {
var data = getData(event); // your custom method to get data, i am just using json here
progress.setAttribute('value', data.filesDone);
progress.setAttribute('max', data.filesTotal);
progress.setAttribute('min', 0);
};
}
request.post("/me/photos",{
files: files
}).then(function(data){
return data.upload_id;
}).then(TrackProgress);
在服務器端,您將需要創建一些東西,保持任務的軌道,一個簡單的工作架構,JOB_ID和進步發送到數據庫就足夠了。我會把工作安排給你和路由,但是在那之後,概念代碼(對於最簡單的SSE就足以滿足上面的代碼)如下。
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
/* Other code to take care of how do you find how many files are left
this is really not required */
function sendStatusViaSSE($task_id){
$status = getStatus($task_id);
$json_payload = array('filesDone' => $status.files_done,
'filesTotal' => $status.files_total);
echo 'data: ' . json_encode($json_payload) . '\n\n';
ob_flush();
flush();
// End of the game
if($status.done){
die();
}
}
while(True){
sendStatusViaSSE($request.$task_id);
sleep(4);
}
?>
上證A很好的教程可以在這裏找到http://html5doctor.com/server-sent-events/
,你可以閱讀更多關於從服務器上這個問題推更新Pushing updates from server
以上是一個概念上的說明,還有其他方法來實現這一點,但這是解決方案,在我的情況下照顧了一個相當大的任務。
這是一個有點困難,(FYI)PHP過程和您的AJAX請求正在由單獨的線程處理,因此您不能得到$progress
值。
一個快速解決方案:您可以在每次更新時將進度值寫入$_SESSION['some_progress']
,然後您的AJAX請求可以通過訪問$_SESSION['some_progress']
來獲取進度值。
您需要使用JavaScript的setInterval()
或setTimeout()
才能繼續調用AJAX處理程序,直到返回100
。
這不是完美的解決方案,但它的快速和簡單。
因爲您不能同時使用同一個會話兩次,請改用數據庫。將狀態寫入數據庫,然後用間隔的AJAX調用讀取數據。
一些開發商使用一些對象緩存模式,該對象寫入某個緩存,所以ajax請求可以獲得相同的對象屬性和值。 –
這聽起來像是一個很好的解決方案。我想聽聽其他答案,如果可能的話,我想避免不必要的'$ _SESSION's。 –
與ajax一起使用APC緩存。使用apc獲取與展示進度上傳相同的進度。你也可以使用Session。這很簡單,可以解決你的問題。 – meotimdihia
你有沒有考慮過輸出JavaScript和使用流刷新?它看起來像這樣
echo '<script type="text/javascript> update_progress('.($progress->get_progress()).');</script>';
flush();
由於刷新,此輸出立即發送到瀏覽器。從長時間運行的腳本定期執行它,它應該工作得很好。
是的,但如果我沒有記錯,瀏覽器不會呈現少於x字節發送。所以這可能是一個問題。但是,注意到。也會嘗試。 –
你是對的,IE可能會緩衝,如果它小於256字節。只需添加一個echo str_repeat(「」,256); –
這是更多的。 Chrome也是如此。我不知道需要多少。儘管如此,當我在電腦旁邊時,我會對它進行測試。 –
我會在這裏把這個誰都搜索參考 - 這依賴於非JavaScript所有..
<?php
/**
* Quick and easy progress script
* The script will slow iterate through an array and display progress as it goes.
*/
#First progress
$array1 = array(2, 4, 56, 3, 3);
$current = 0;
foreach ($array1 as $element) {
$current++;
outputProgress($current, count($array1));
}
echo "<br>";
#Second progress
$array2 = array(2, 4, 66, 54);
$current = 0;
foreach ($array2 as $element) {
$current++;
outputProgress($current, count($array2));
}
/**
* Output span with progress.
*
* @param $current integer Current progress out of total
* @param $total integer Total steps required to complete
*/
function outputProgress($current, $total) {
echo "<span style='position: absolute;z-index:$current;background:#FFF;'>" . round($current/$total * 100) . "% </span>";
myFlush();
sleep(1);
}
/**
* Flush output buffer
*/
function myFlush() {
echo(str_repeat(' ', 256));
if (@ob_get_contents()) {
@ob_end_flush();
}
flush();
}
?>
偉大的例子!非常感謝 ! –
爲什麼我們需要「睡眠(1)」?完成腳本需要更多時間。 – Sithu
這是一個很好的方法,但是當數組很大時,需要小心,因爲它每次都會輸出一個新的DOM元素。我建議檢查一個mod運算符的百分比(%)。 –
解決辦法是:
阿賈克斯投票 - 在服務器端將進度存儲在某個地方,然後使用ajax調用以定期獲取進度。
服務器發送事件 - 一個html5功能,允許從服務器發送輸出生成dom事件。這是針對這種情況的最佳解決方案,但IE 10不支持它。
腳本/ Iframe流式傳輸 - 使用iframe流式處理長時間運行腳本的輸出,該腳本將輸出腳本標記作爲可在瀏覽器中產生一些反應的時間間隔。
這是一個老問題,但我有類似的需求。我想用php system()
命令運行腳本並顯示輸出。
我這樣做沒有投票。
對於二Rikudoit情況應該是這樣的:
的JavaScript
document.getElementById("formatRaid").onclick=function(){
var xhr = new XMLHttpRequest();
xhr.addEventListener("progress", function(evt) {
var lines = evt.currentTarget.response.split("\n");
if(lines.length)
var progress = lines[lines.length-1];
else
var progress = 0;
document.getElementById("progress").innerHTML = progress;
}, false);
xhr.open('POST', "getProgress.php", true);
xhr.send();
}
PHP
<?php
header('Content-Type: application/octet-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
// Implicitly flush the buffer(s)
ini_set('implicit_flush', true);
ob_implicit_flush(true);
// Clear, and turn off output buffering
while (ob_get_level() > 0) {
// Get the curent level
$level = ob_get_level();
// End the buffering
ob_end_clean();
// If the current level has not changed, abort
if (ob_get_level() == $level) break;
}
while($progress < 100) {
// STUFF TO DO...
echo '\n' . $progress;
}
?>
雖然這個答案是正確的,我覺得不安,upvoting它,因爲它使用jQuery。你已經在自己做XHR操作,所以它不會添加任何東西 - 如果你從這個中刪除jQuery,我會很樂意提升它。 –
也許可以贏得賞金;) –
經常在web應用中,我們可能到後端系統的請求,其可能會觸發長時間運行的過程,如搜索大量數據或長時間運行的數據庫進程。然後前端網頁可能會掛起並等待處理完成。在此過程中,如果我們能夠向用戶提供有關後端進程進度的一些信息,則可能會改善用戶體驗。不幸的是,在Web應用程序中,這似乎並非易事,因爲Web腳本語言不支持多線程,而HTTP是無狀態的。我們現在可以通過AJAX來模擬實時過程。
基本上我們需要三個文件來處理請求。第一個是運行實際長時間運行作業的腳本,它需要一個會話變量來存儲進度。第二個腳本是狀態腳本,它將在長時間運行的作業腳本中響應會話變量。最後一個是可以頻繁輪詢狀態腳本的客戶端AJAX腳本。
對於執行細節,你可以參考PHP to get long running process progress dynamically
在這裏,我只是想補充的是什麼@Jarod凌駕於法律之上https://stackoverflow.com/a/7049321/2012407
非常簡單而有效的確實寫了前2個擔憂。我調整和二手:) 所以我的2個擔憂是:
,而不是使用setInterval()
或setTimeout()
使用遞歸調用回調,如:
function trackProgress()
{
$.getJSON(window.location, 'ajaxTrackProgress=1', function(response)
{
var progress = response.progress;
$('#progress').text(progress);
if (progress < 100) trackProgress();// You can add a delay here if you want it is not risky then.
});
}
由於通話是在五月異步否則以不需要的順序返回。
保存到$_SESSION['some_progress']
是聰明的,你可以,不需要數據庫存儲。 實際需要的是允許同時調用這兩個腳本,而不是由PHP排隊。所以你最需要的是session_write_close()
! 我在這裏發表非常簡單的演示示例:https://stackoverflow.com/a/38334673/2012407
您可能會感興趣的[這個答案](http://stackoverflow.com/a/25104147/731138) –