php flush的问题已经碰到过两次了,一次是在做毕设期间,由于需要php去执行一个非常大的数据处理进程,想要显示一个实施进度。目前这次是需要php拉起一个python进程去对一个文件进行处理,想要显示一个文件状态。
由于涉及Apache、php、浏览器,无法准确定位原因。每次都耗费了大量的时间,同时网上现有的解决方案几乎都无法正确执行。
环境 Apache/2.4.27 (Win64)+PHP 7.0.22 (cli) (built: Aug 1 2017 13:56:43) ( ZTS )+Chrome版本 63.0.3239.132(正式版本) (32 位)
Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php header ('Content-Type: text/HTML; charset=utf-8' );ob_start ();set_time_limit (0 );for ($i =0 ; $i <10 ; $i ++){ $flush = str_pad ('' , 1000 ); echo $flush ; echo "console.log(" .$i .');' ; ob_flush (); flush (); sleep (1 ); } ?>
上述代码适用于直接输出结果到页面,而不能返回结果给ajax。
在Ajax访问时不成功的原因是:请求都没有完成,XMLHttpRequest组件只负责交换数据,不负责处理数据。处理数据的代码要等到通讯结束后才执行。如果Ajax不以XMLHttpRequest 做传输载体,而以 iframe 做载体的话,上述代码是可以成功的。
实现实时输出的方案 1 提示: 从以下php代码中可以看到每次循环都执行了sleep(1),如果sleep的时间过短例如sleep(0.1)则以下代码并不能良好的执行,仍存在同时返回多条信息的情景。为了应对这种情景我们仍需要一些hack操作,如果状态较少或者循环次数较少则可在每次状态改变并且echo信息之后执行sleep(1),如果状态相应过快或者循环次数较多时,需要改造前端程序,使其具备同时处理多条数据(信息)的能力。
AJAX - XHR Method Demo
这里采用标准的XHR方法,不是等待readyState == 4时响应请求而是在readyState == 3就对请求进行处理。
1 2 3 4 5 6 ReadyState, holds the status of the XMLHttpRequest 0: request not initialized //未初始化请求 1: server connection established //建立服务器连接 2: request received //收到请求 3: processing request //处理请求 4: request finished and response is ready //请求已完成,响应已准备就绪
对应的js代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 if (!window .XMLHttpRequest ){ alert ("Your browser does not support the native XMLHttpRequest object." ); return ; } try { var xhr = new XMLHttpRequest (); xhr.previous_text = '' ; xhr.onerror = function ( ) { alert ("[XHR] Fatal Error." ); }; xhr.onreadystatechange = function ( ) { try { if (xhr.readyState == 4 ){ alert ('[XHR] Done' ) } else if (xhr.readyState > 2 ){ var new_response = xhr.responseText .substring (xhr.previous_text .length ); var result = JSON .parse ( new_response ); document .getElementById ("divProgress" ).innerHTML += result.message + ' ' ; document .getElementById ('progressor' ).style .width = result.progress + "%" ; xhr.previous_text = xhr.responseText ; } } catch (e){ alert ("[XHR STATECHANGE] Exception: " + e); } }; xhr.open ("GET" , "ajax_stream.php" , true ); xhr.send (); } catch (e){ alert ("[XHR REQUEST] Exception: " + e); }
php代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 set_time_limit (0 ); ob_implicit_flush (true ); for ($i = 0 ; $i < 10 ; $i ++){ sleep (1 ); $p = ($i +1 )*10 ; $response = array ( 'message' => $p . '% complete. server time: ' . date ("h:i:s" , time ()), 'progress' => $p ); echo json_encode ($response ); } sleep (1 );$response = array ( 'message' => 'Complete' , 'progress' => 100 ); echo json_encode ($response );
Iframe Method Demo
这个方法比较老,但胜在有效。通过创建一个ifream来执行某个php文件避免等待数据传输结束
1 2 3 4 5 6 ifrm = document .createElement ("IFRAME" ); ifrm.setAttribute ("src" , "ajax_stream.php" ); ifrm.style .width = 0 +"px" ; ifrm.style .height = 0 +"px" ; ifrm.style .border = 0 ; document .body .appendChild (ifrm);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 set_time_limit (0 ); ob_implicit_flush (true );for ($i = 0 ; $i < 10 ; $i ++){ sleep (1 ); $p = ($i +1 )*10 ; $message = $p . '% complete. server time: ' . date ("h:i:s" , time ()); $progress = $p ; echo '' ; } sleep (1 );$message = 'Complete' ;$progress = 100 ;echo '' ;
参考 & 引用(ps 谷歌大法好) http://blog.csdn.net/u011832039/article/details/51387548 https://segmentfault.com/q/1010000008402305/a-1020000008408369 http://www.webhostingtalk.com/showthread.php?t=1138631 http://stratosprovatopoulos.com/web-development/php/ajax-progress-php-script-without-polling/