我想在我的网站中创建一个部分,其中用户只有几个简单的update
按钮。
这些update
按钮中的每一个都将进入服务器,并将在幕后进行长时间的处理。
当服务器处理数据时,我希望用户拥有某种进度指示器,例如进度条或文本百分比。
我正在使用jQuery作为我的JavaScript库,而CodeIgniter(PHP)作为服务器端框架,如果它很重要......
我在想的是使用PHP的flush()
函数向jQuery报告进度状态,但我不确定jQuery的Ajax函数在完成之前是否正在读取输出...
所以任何建议/解释都会有用且有用!
答案 0 :(得分:3)
我将使用WebSync On-Demand给您一个示例,但无论您选择哪种服务器,都可以使用相同的方法。
这是你做的。首先,以某种方式开始长期运行;你的用户点击按钮开始这个过程(我将假设一个Ajax调用,但无论什么工作),你返回给他们某种标识符,我们称之为'myId',给它一个值' 1。你是否通过调用某种过程来做到这一点取决于你。
然后,在你的调用回调中,你会写这样的东西:
var myId = 1; // this would be set somewhere else
client.initialize('api key');
client.connect();
client.subscribe({
channel: '/tasks/' + myId,
onReceive: function(args){
// update the progress bar
myProgressBar.update(args.data.progress);
}
});
这样做是订阅您的客户端以接收有关任务更新的通知,因此剩下的就是推出更新,您将在实际运行任务的任何进程中执行更新。这看起来像(在PHP中,使用SDK):
$publisher = new Publisher(
"11111111-1111-1111-1111-111111111111", // your api key again
"mydomain.com" // your domain
);
// publish data
$response = $publisher->publish(array(
array(
'channel' => '/tasks/' . $myId, //comes from somewhere
'data' => (object) array(
'progress' => '45' //45% complete
)
)
));
// success if empty (no error)
$success = empty($response);
就是这样;当更新发生时,它们会实时向您的客户推送。
答案 1 :(得分:2)
很难做到这一点。我们为我们的系统确定的是一个“伪造”的进度条 - 它只是一遍又一遍地动画(因为它是一个动画的GIF,你可能会期望!)。
另一种方法是提交一个脚本,并在后台进行处理(并将进度输出到文件),同时向另一个脚本发出Ajax请求,该脚本的唯一责任是读取该进度文件并返回多远你是谁。这会有用 - 感觉有点笨拙,但它至少可以解决你的问题。
我对Comet或喜欢的东西知之甚少,所以这完全基于我目前的理解。
答案 2 :(得分:0)
晚了3年,但这是我提出的解决方案。奖励:它适用于IE7 +
用途:
事件表:
create table updates(
evt_id int unsigned not null auto_increment,
user_id int unsigned not null,
evt_type enum('start','update','finish') not null,
evt_msg varchar(255) not null,
primary key (evt_id)
)
HTML:
<?php
include 'libconfig.php';
session_write_close();
if(count($_POST)){
$db=db_get_connection();
$stm=new PDOStatementWrapper(db_prepare($db,'INSERT INTO bupdates VALUES (:event_id,:user_id,:type,:message)'));
if($stm->run(array(
':event_id'=>0,
':user_id'=>App::user()->getId(),
':type'=>$_POST['type'],
':message'=>$_POST['message']
)))echo 'Inserted';
return;
}
?>
<!doctype html>
<html>
<head>
<title>tester</title>
<link rel=stylesheet href="s/jquery-ui-1.10.3.custom.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="js/eventsource.js"></script>
<script src="js/json2.js"></script>
<script>
var MixerStatusMonitor=(function(){
var _src=null,
_handler={
onStart:function(e){
MixerStatus.setMax(parseInt(e.data));
},
onUpdate:function(e){
var data=JSON.parse(e.data);
MixerStatus.setValue(parseInt(data.progress));
MixerStatus.setStatus(data.message);
},
onFinish:function(e){
//var data=JSON.parse(e.data);
MixerStatus.hide();
_src.close();
}
};
return {
init:function(){
if(_src)_src.close();
_src=new EventSource('/daemon/updates.php?type=b');
_src.addEventListener('update',_handler.onUpdate,false);
_src.addEventListener('start',_handler.onStart,false);
_src.addEventListener('finish',_handler.onFinish,false);
MixerStatus.show();
}
};
})();
var MixerStatus=(function(){
var dialog=null,pbar=null,text=null;
return {
init:function(){
dialog=$('#buildStatus').dialog({autoOpen:false});
pbar=$('#buildStatus .progress').progressbar({value:false});
text=$('#buildStatus .text').progressbar();
},
setStatus:function(txt){
text.html(txt);
},
setMax:function(val){
pbar.progressbar('option','max',val);
},
setValue:function(val){
pbar.progressbar('option','value',val);
},
show:function(){
dialog.dialog('open');
},
hide:function(){
dialog.dialog('close');
}
};
})();
$(document).ready(function(){
MixerStatus.init();//build the UI
$('#updater').on('submit',function(){
$.ajax({
type:'post',
url:'test-updates.php',
data:$('#updater').serialize(),
beforeSend:function(){
if($('#updater select[name=type]').val()=='start'){
MixerStatusMonitor.init();
}
}
});
return false;
});
});
</script>
</head>
<body>
<p>Start event sets the max
<p>update event: {"progress":"","message":""}
<p>finish event: {"progress":"","message":""}
<form id=updater>
message: <input type=text name=message value="15"><br>
event type: <select name=type>
<option value=start>start</option>
<option value=update>update</option>
<option value=finish>finish</option>
</select><br>
<button>send message</button>
</form>
<div id=buildStatus title="Building">
<div class=text></div>
<div class=progress></div>
</div>
<div id=messages></div>
</body>
</html>
PHP:
<?php
header('Content-Type: text/event-stream');
define('TYPE_BROADCAST','b');
define('MAX_FAILURES',30);//30 seconds
define('MAX_WAIT',30);//30 seconds
define('MAX_START_WAIT',6);//30 seconds
/*
* URL arguments:
* type
*/
include '../libconfig.php';
session_write_close();
if(!App::loggedIn() || !App::user()){
printEvent(0,'finish','Login session has expired.');
}
if($_GET['type']==TYPE_BROADCAST){//not needed;specific to the app I am creating
$db=db_get_connection();
$stm=new PDOStatementWrapper(db_prepare($db,'SELECT * FROM updates WHERE user_id=:user_id AND evt_id>:last_id'));
$args=array(':user_id'=>App::user()->getId(),':last_id'=>0);
$stm->bindParam(':user_id',$args[':user_id'],PDO::PARAM_INT);
$stm->bindParam(':last_id',$args[':last_id'],PDO::PARAM_INT);
$failures=0;
$nomsg=0;
if(!isset($_SERVER['HTTP_LAST_EVENT_ID'])){
$start=new PDOStatementWrapper(db_prepare($db,'SELECT * FROM updates WHERE user_id=:user_id ORDER BY evt_id DESC'));
$start->bindValue(':user_id',$args[':user_id'],PDO::PARAM_INT);
$startwait=0;
while(1){
if($startwait>MAX_START_WAIT){
printEvent(0,'finish','Timed out waiting for the process to start.');
return;
}
sleep(5);
$startwait++;
if(!$start->run()){
printEvent(0,'finish','DB error while getting the starting event.');
return;
}
while($start->loadNext()){
if($start->get('evt_type')=='finish')continue 2;
if($start->get('evt_type')=='start')break;
}
if($start->get('evt_type')=='start'){
$args[':last_id']=$start->get('evt_id');
printEvent($start->get('evt_id'),'start',$start->get('evt_msg'));
break;
}
}
}else
$args[':last_id']=$_SERVER['HTTP_LAST_EVENT_ID'];
if($args[':last_id']===0){
printEvent(0,'finish','ll');
exit;
}
while(1){
sleep(1);
if(!$stm->run()){
$failures++;
if($failures>MAX_FAILURES){
printEvent(0,'finish','Max failures reached.');
break;
}
}
if($stm->loadNext()){
$failures=0;
$nomsg=0;
do{
if($stm->get('evt_type')=='finish')break;
$args[':last_id']=$stm->get('evt_id');
printEvent($stm->get('evt_id'),$stm->get('evt_type'),$stm->get('evt_msg'));
}while($stm->loadNext());
if($stm->get('evt_type')=='finish'){
printEvent($args[':last_id'],'finish',$stm->get('evt_msg'));
break;
}
}else{
$nomsg++;
if($nomsg>MAX_WAIT){
exit;//TODO: test
}
}
}
}else{
printEvent(0,'close','Unknown event type.');
}
function printEvent($id,$name,$data){
echo "id: $id\nevent: $name\n";
if(is_array($data)){
foreach($data as $datum)
echo "data: $datum\n";
echo "\n";
}else
echo "data: $data\n\n";
flush();
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
$_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest')exit;//ajax request. Need to kill the connection.
}
如果您想知道PDOStatementWrapper
的来源是here。对不起,它不包含任何与CodeIgniter集成的内容。