我的客户端不断测试我在他的移动浏览器上制作的脚本......其中之一是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