2

我想在我的网站中创建一个部分,其中用户有几个简单的update按钮。

这些update按钮中的每一个都将进入服务器,并将在幕后进行长时间的处理。

当服务器处理数据时,我希望用户有某种进度指示器,例如进度条或文本百分比。

我使用 jQuery 作为我的 JavaScript 库,使用 CodeIgniter (PHP) 作为服务器端框架,如果它很重要的话......

我在想的是使用 PHP 的flush()函数向 jQuery 报告进度状态,但我不确定 jQuery 的 Ajax 函数是否在完成之前读取输出......

因此,任何建议/解释都会很有用和有帮助!

4

3 回答 3

4

我将为您提供一个使用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); 

就是这样; 随着更新的发生,它们会实时推送给您的客户。

于 2010-03-22T15:09:28.460 回答
2

很难做到这一点。我们为我们的系统确定的是一个“伪造的”进度条——它只是一遍又一遍地动画(因为它是一个动画 gif,你可能会想到!)。

另一种方法是提交到一个脚本,并在后台进行处理(并将进度输出到文件),同时向另一个脚本发出 Ajax 请求,该脚本的唯一职责是读取该进度文件并返回您完成的过程多远是。这会起作用 - 感觉有点笨拙,但它至少会解决你眼前的问题。

我对彗星之类的东西知之甚少,所以这纯粹是基于我目前的理解。

于 2010-03-15T10:22:12.103 回答
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它的来源在这里。抱歉,它不包含与 CodeIgniter 集成的任何内容。

于 2013-05-16T19:22:28.740 回答