我的客戶端不斷測試我在他的移動瀏覽器上製作的腳本......其中之一是Opera「迷你」。在某個時候,一個進程需要運行幾分鐘,而我不知道如何在這個瀏覽器上處理這個問題。我想首先展示進度,但此時我只是想通過任何方式將瀏覽器置於保持狀態,直到過程完成並在接收到通知時進行通知。
我知道或嘗試過的東西:
- Opera mini不支持XMLHTTPRequest 2.0。所以你不能以這種方式獲得進展。
- 它支持定時器,但只支持5秒...所以你不能繼續發送AJAX請求來檢查進度。
- 我試圖發送一個AJAX請求來完成這個工作,而只是等待成功回調,但似乎瀏覽器在很長一段時間後超時了AJAX請求。
- 「你不能把這個過程分成小部分嗎?」你會說。我這樣做,並重新加載每個子運行的頁面......直到我意識到了缺點:如果你想回到瀏覽器,你會看到同一頁面50倍。
有沒有辦法處理這個?我會很感激任何想法。謝謝!
在移動「迷你」瀏覽器上運行大型PHP進程
回答
最近我遇到了類似的問題。製作Ajax請求將有兩個問題。第一個,導航員將被凍結。第二,大多數服務器在腳本運行一段時間後會產生錯誤(在某些情況下,通常可以提高30秒)。
我第一次處理它的方式是將相關數據記錄在文件中,並將進程分成較小的進程,並且在每次成功執行ajax響應時,重新啓動下一步,直到任務結束,將%完成保存到會話對每個請求,以及過程的當前步驟變量來恢復它,就像這樣:
function stepOnTask(){
ajax.post("file.php", "action:doPartialTask", function(response){
if (response.taskFinished) alert("Task Done");
else{
showProgress(response.currentProgress);
stepOnTask();
}});
}
,但它是我的桌面導航確實激烈,往往墜毀,不是說安:我我什麼都不做,所以我完全改變了另一種方法,在php中使用後臺進程,並在啓動進程的pid命名的文件中保存相關信息(預計時間,開始時間等...)一個d每隔x秒向該文件發送一個請求,以檢查並顯示進度。
最後一點稍微長一點,如果您不要求我發帖,我不會發布代碼,因爲我不確定這是您尋求的解決方案。
祝你好運。
編輯
PHP後臺進程風格
Class BackgroundScript{
public $win_path = "C:\\xampp\\htdocs\\www\\yourProject";
public $unix_path = "/home/yourFolder/yourProject.com";
public $script = NULL;
public $command = NULL;
public $pid = NULL;
public $start_time = NULL;
public $estimated_time = NULL;
public $ellapsed_time = NULL;
public $status_file = NULL;
public function __construct($script = ""){
$this->script = $script;
if (self::get_os() == "windows"){
$this->command = "start /b C:\\xampp\\php\\php.exe ".$this->win_path."\\".$this->script;
}else{
$this->command = "php ".$this->unix_path."/".$this->script;
}
}
public static function conPID($pid){
if (file_exists(dirname(__FILE__)."/pids/".$pid.".json")){
$bgScript = new BackgroundScript();
$process_info = json_decode(file_get_contents(dirname(__FILE__)."/pids/".$pid.".json"));
foreach ($process_info as $key=>$val){
$bgScript->$key = $val;
}
return $bgScript;
}else {
return false;
}
}
public static function get_os(){
if (substr(php_uname(), 0, 7) == "Windows") return "windows";
else return "unix";
}
public function saveToFile(){
$path_to_pfolder = self::get_os()=="windows"? $this->win_path."\\pids":$this->unix_path."/pids";
if (!(file_exists($path_to_pfolder) && is_dir($path_to_pfolder))){
mkdir($path_to_pfolder);
}
$fileHandler = fopen($path_to_pfolder."/".$this->pid.".json", "w");
$this->status_file = $path_to_pfolder."/".$this->pid.".json";
fwrite($fileHandler, json_encode($this));
fclose($fileHandler);
return $this->status_file;
}
public function removeFile(){
$path_to_pfolder = self::get_os()=="windows"? $this->win_path."\\pids":$this->unix_path."/pids";
unlink($path_to_pfolder."/".$this->pid.".json");
}
public function run($outputFile = '/dev/null'){
if (self::get_os() == "windows"){
$desc = array(
0 => array("pipe", "r"), // stdin es una tubería usada por el hijo para lectura
1 => array("pipe", "w"), // stdout es una tubería usada por el hijo para escritura
);
//proc_get_status devuelve el pid del proceso que lanza al proceso, o sea, del padre, y hay que hacer algo más para obtener el pid real del proceso que genera el archivo
$p = proc_open($this->command, $desc, $pipes);
$status = proc_get_status($p);
$ppid = $status["pid"];
//Ya tenemos el pid del padre, ahora buscamos el del último proceso hijo, que será el que acabamos de lanzar, y lo guardamos
$output = array_filter(explode(" ", shell_exec("wmic process get parentprocessid,processid | find \"$ppid\"")));
array_pop($output);
$this->pid = end($output);
//Cerramos el proceso padre, esto es lo que hará que no se nos quede pillada la aplicación mientras el "servidor" trabaja.
proc_close($p);
} else{
//En unix e ma facilico
$this->pid = trim(shell_exec(sprintf('%s > %s 2>&1 & echo $!', $this->command, $outputFile)));
}
$this->ellapsed_time = 0;
$this->start_time = date("Y-m-d H:i:s");
return $this->saveToFile();
}
public function isRunning()
{
try {
$result = shell_exec(sprintf('ps %d', $this->pid));
if(count(preg_split("/\n/", $result)) > 2) {
return true;
}
} catch(Exception $e) {}
return false;
}
public function kill(){
$this->removeFile();
if (self::get_os() == "windows"){
shell_exec(" taskkill /PID ".$this->pid);
} else{
// shell_exec(sprintf('kill %d 2>&1', $this->pid));
shell_exec(sprintf('kill '.$this->pid));
}
}
public function getPid(){
return $this->pid;
}
public static function getAll(){
$path_to_pfolder = self::get_os()=="windows"? self::$win_path."\\pids":self::$unix_path."/pids";
if (!(file_exists($path_to_pfolder) && is_dir($path_to_pfolder))){
return array();
}
$archivos = scandir($path_to_pfolder);
$processes = array();
foreach ($archivos as $archivo){
if (is_file($path_to_pfolder."/".$archivo)){
$json = file_get_contents($path_to_pfolder."/".$archivo);
$info = json_decode($json);
$process = new BackgroundScript();
foreach ($info as $key=>$val){
$process->$key = $val;
}
$processes[] = $process;
}
}
return $processes;
}
public function view(){
$segundos_estimados = $this->estimated_time;
$segundos_transcurridos = time() - strtotime($this->start_time);
$segundos_restantes = max($segundos_estimados - $segundos_transcurridos, 0);
/*
$minutos_estimados = floor($segundos_estimados/60);
$segundos_estimados = $segundos_estimados - $minutos_estimados*60;
$minutos_restantes = floor($segundos_restantes/60);
$segundos_restantes = $segundos_restantes - $minutos_restantes*60;
*/
$estimado = date("i:s", strtotime("1983-09-23 00:00:00")+$segundos_estimados);
$restante = date("i:s", strtotime("1983-09-23 00:00:00")+$segundos_restantes);
if (!$segundos_estimados){
$html="<a>".$this->nombre_diario."
<!--<br>Tiempo Estimado: <span class='estimado'>Calculando</span>-->
<br>Tiempo Restante: <span class='restante' data-time='".$segundos_restantes."'>Calculando</span></a>";
}elseif (!$segundos_transcurridos){
$html="<a>".$this->nombre_diario."
<!--<br>Tiempo Estimado: <span class='estimado'>Guardando</span>-->
<br>Tiempo Restante: <span class='restante' data-time='".$segundos_restantes."'>Guardando</span></a>";
}else{
$html="<a>".$this->nombre_diario."
<!--<br>Tiempo Estimado: <span class='estimado'>".$estimado."</span>-->
<br>Tiempo Restante: <span class='restante' data-time='".$segundos_restantes."'>".$restante."</span></a>";
}
return $html;
}
}
好吧,我理解的代碼可能看起來有點壞,但它的工作原理。
現在,我會告訴你我使用它的方式,你必須適應你自己的風格。
我有一個叫Controller.php這樣的文件來處理我的項目,看起來很像這一切的動作:
if (isset($_POST) && isset($_POST["action"])) $action= $_POST["action"];
else $action= $argv[1];
switch ($action) {
case "performTask1":{
task1();
}
break;
case "performTask2":{
task2();
}
break;
case "performTask2inBackground":
{
$process = new BackgroundScript("controller.php performTask2");
$response["file_with_info"] = $process->run();
}
break;
echo json_encode($response);
}
而且that's所有。
當然,在課程開始時,您必須更改win_path和unix_path以將您自己的機器路徑與項目相匹配。我使用它們兩個,所以我的本地測試環境和真正的服務器版本工作相同。沒有Mac版本:P(希望你不需要它)。
另一件需要注意的是,在構造函數中,如果您的php文件夾位於不同的路徑中,您可能必須更改構建變量「command」的路徑。
將在項目的根目錄下創建一個名爲「pid」的目錄,以保存帶有名稱爲{pid_of_the_process} .json的信息的文件。請注意,如果您沒有填寫有用的信息,您可以自行決定是否填寫有用的信息,但不會提供有用的信息。
正確的方式做,這會是這樣,在您的腳本,做這樣的事情:
...
do{
doLotsOfThings();
$bgScript= BackgroundScript::conPID(getmypid());
$bgScript->estimated_time = recalculate_estimated_time();
$bgScript->ellapsed_time = recalculate_remaining_time();
$bgScript->saveToFile();
} while($whatever)
//END
$process->kill();
要檢索大約在正在運行的進程爲任何目的的任何一點信息,您可以使用BackgroundScript::getAll();
,展現例如,估計剩餘時間的快照,例如,這就是爲什麼留下一個「視圖」方法,這對您可能沒有用處,但是用於檢索狀態並向用戶顯示剩餘時間。
出於調試目的,我建議你找到PHP錯誤日誌文件,急需的,因爲你不會致使有直接的瀏覽器反饋,請記住,你可以簡單地在控制檯粘貼生成的命令和運行,如果該進程你想知道發生了什麼的第一手資料。
最後,我想給@FlorianEckerstorfer留下信用,後者的後臺程序庫幫助我開發了我在此發佈的解決方案。
出於某種原因,我沒有想到這一點,即使我以前使用過它:分成大塊,只需爲每個成功響應做一個新的AJAX調用......所以沒有定時器參與,也沒有長時間的AJAX調用。謝謝! – 2014-11-06 18:29:52
值得一試,但它經常會讓我的超級計算機上的Google Chrome崩潰,所以對Opera Mini不太樂觀,讓我知道它是否可行。 – sergio0983 2014-11-06 18:37:44
不幸的是...沒有,沒有工作。儘管我無法在任何地方找到確認,Opera mini似乎在加載頁面5秒後取消了任何AJAX請求。瀏覽器只是拒絕以任何方式使用處理器的時間長於此時間。 – 2014-11-07 20:50:22
您不能發送分塊響應給用戶,以便他繼續在他的網頁上看到結果,同時進程繼續處理新數據。
// Turn off output buffering
ini_set('output_buffering', 'off');
// Turn off PHP output compression
ini_set('zlib.output_compression', false);
//Flush (send) the output buffer and turn off output buffering
//ob_end_flush();
while (@ob_end_flush());
// Implicitly flush the buffer(s)
ini_set('implicit_flush', true);
ob_implicit_flush(true);
echo '
<table>
<thead>
<th>Url</th>
<th>Id</th>
<th>Class</th>
</thead>
<tbody>
';
ob_flush();
flush();
您可以通過Google瞭解有關Chuncked響應的更多詳細信息。
謝謝。在研究過程中,我遇到過這樣的解決方法。我以前沒有用過這樣的東西,我想知道:這是用來在文檔的末尾總是「追加」HTML的,對吧?但是,您可以在執行過程中以某種方式更新進度顯示嗎? (沒有計時器) – 2014-11-06 18:37:33
是的,你是對的。此方法將以塊的形式發送數據,並且主要用於渲染頁面渲染速度快的錯覺。但如果你確實迴應'數據';使用ob_flush();沖洗();在for循環中,如果你可以用進度更新你的客戶端,那麼它可以解決你的問題。例如:foreach($ data爲$ key => $ item){echo $ item。 ''。$ key。'%completed';使用ob_flush();沖洗();}。 – 2014-11-15 08:45:59
如果你不需要服務器響應,你的頁面可以嘗試加載一些1x1px圖像。這img是php腳本什麼返回這img,然後重置連接。但是使用ignore_user_abort(true)腳本仍然可以繼續工作。
- 1. 迷你javascript瀏覽器
- 2. 迷你圖片瀏覽器(如myfonts.com上)
- 3. jQuery沒有在移動瀏覽器上運行
- 4. 如何在桌面瀏覽器上運行移動joomla?
- 5. 如何僅在非移動瀏覽器上運行jQuery?
- 6. 從Windows的瀏覽器在後臺運行PHP進程
- 7. 如何強制文件在移動瀏覽器上使用PHP進行下載?
- 8. 低迷的UI /在移動Safari瀏覽器(HTML5畫布)
- 9. 你如何知道Node.js代碼將在瀏覽器上運行?
- 10. 在瀏覽器中運行PHP以進行假IP投票?
- 11. 在移動瀏覽器上測試WML
- 12. DIV移動時放大和瀏覽器
- 13. HTML/CSS:我的迷你聊天在IE瀏覽器中溢出
- 14. 在Web瀏覽器中運行WPF瀏覽器應用程序
- 15. 如何在Safari瀏覽器(Web瀏覽器)運行小程序
- 16. 移回瀏覽器的運行實例
- 17. 防止按鈕在瀏覽器上移動re大小
- 18. 在ipad瀏覽器上運行karma e2e
- 19. ChildBrowser在非移動瀏覽器中進行測試/調試
- 20. 移動瀏覽器和舊瀏覽器上的內容溢出
- 21. 在Windows窗體運行期間移動web瀏覽器C#
- 22. 排除在移動瀏覽器中運行的代碼
- 23. 在調整瀏覽器大小時移動網頁上的移動項目
- 24. jquery 1.9瀏覽器在Safari瀏覽器中移動滾動
- 25. 你如何在Web瀏覽器上WP7
- 26. 移動瀏覽器上的Ajax錯誤
- 27. 移動網頁瀏覽器上的HTML5
- 28. 移動瀏覽器上的JSONP
- 29. 移動瀏覽器上的圖像
- 30. 如何在瀏覽器進程被終止時運行javascript?
1)作業在某個地方定期寫入狀態2)服務器端ajax處理程序讀取狀態3)客戶機週期性地請求狀態。 – 2014-11-06 17:27:40