使用服务器发送事件在 cakephp 3 中实现数据库插入进度条

Implementing Database Inserting progress bar in cakephp 3 using Server Sent Events

我有一个包含大量数据的 csv 文件。目标是将此数据导入数据库。最初文件在客户端处理,然后在客户端验证后,将其发送到服务器以进行数据库插入。

在我的服务器端,我使用 cakephp 3 框架。

我需要的是从控制器操作向客户端提供更新(即插入数据的百分比)。

我进行了研究,偶然发现了可用于该解决方案的服务器发送事件。如果我让下面的代码工作,我就能弄清楚如何将进度通知客户端。但是,当我试用示例代码时,我不断收到 readyState 等于 EventSource.CONNECTING.

的 'error' 事件

这是从 SSE 教程站点获取的示例代码,我只是尝试将其应用于 cakephp 3:

index.ctp

<button id="btnStartTheClock">start</button>
<button id="btnStopTheClock">stop</button>
<div id="result"></div>


<script type="text/javascript">
var essSupport = false, es, result;
function init() {
    esSupport = (window.EventSource !== undefined);
    result = document.getElementById("result");
    document.getElementById("btnStartTheClock").onclick = startTheClock;
    document.getElementById("btnStopTheClock").onclick = stopTheClock;
}

window.onload = init;

function startTheClock() {
    if(esSupport) {
        if (es === undefined) {
            es = new EventSource("import-progress");
            es.addEventListener('message', function (event) {
                result.innerHTML += event.data + '<br/>';
            });
            es.addEventListener('open', function (e) {
                result.innerHTML += "<span style='color:green;'>SSE open</span><br/>";
            });
            es.addEventListener('error', function(e) {
                e = e || event, msg = '';

                switch( e.target.readyState ){
                // if reconnecting
                case EventSource.CONNECTING:
                    msg = 'Reconnecting…';
                    break;
                // if error was fatal
                case EventSource.CLOSED:
                    msg = 'Connection failed. Will not retry.';
                    break;
                }
                result.innerHTML += '<span style="color:red;">' + msg + "</span><br>";
            });
        }
    }
    else {
        result.innerHTML = "Your browser doesn't support server-sent events.";
    }
}

function stopTheClock() {
    if(esSupport) { 
        es.close(); 
        result.innerHTML += "Clock Stopped.<br>";
    }
}
</script>

SamplesController.php

<?php
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Controller\Component\RequestHandlerComponent;
use Cake\Event\Event;


class SamplesController extends AppController
{
    public function initialize()
    {
        parent::initialize();
        // $this->loadComponent('RequestHandler');
    }

    public function index()
    {
    }

    public function importProgress()
    {
        $this->set('datetime', date("h:i:s"));
    }
}

import_progress.ctp

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    header('Connection: keep-alive');

    echo "data: The server time is: {$datetime}\n\n";
    flush();
?>

如果你愿意:

旁注:

谢谢!

我会考虑使用 websockets 而不是 SSE——它似乎获得了更多的关注——然后你就可以使用例如。 Ratchet or ReactPHP.

另一件事 - 将进度保存在您的数据库中可能更容易,只需向端点发出 AJAX 请求,每隔 x 秒检查此作业的 status/progress。

例如,如果您使用 https://github.com/dereuromark/cakephp-queue - you can see an example of how to update a job's status here: https://github.com/dereuromark/cakephp-queue/tree/master/docs#updating-status

这是一个老话题:

Show progress for long running PHP script

记得从服务器端关闭循环,以防止不必要的循环。 Websocket 主要用于持续的持久连接,而不是像这样的一次性任务。没有意义 运行 websocket 无缘无故地监听循环。