如何实时读取和回显正在服务器上写入的上传文件的文件大小,而又不会在服务器和客户端上造成阻塞?

如何实时读取和回显正在服务器上写入的上传文件的文件大小,而又不会在服务器和客户端上造成阻塞?,第1张

如何实时读取和回显正在服务器上写入的上传文件文件大小,而又不会在服务器和客户端上造成阻塞?

您需要清除statcache以获取实际文件大小。固定了一些其他位之后,您的stream.php可能如下所示:

<?phpheader("Content-Type: text/event-stream");header("Cache-Control: no-cache");header("Connection: keep-alive");// Check if the header's been sent to avoid `PHP Notice:  Undefined index: HTTP_LAST_EVENT_ID in stream.php on line `// php 7+//$lastId = $_SERVER["HTTP_LAST_EVENT_ID"] ?? 0;// php < 7$lastId = isset($_SERVER["HTTP_LAST_EVENT_ID"]) ? intval($_SERVER["HTTP_LAST_EVENT_ID"]) : 0;$upload = $_GET["filename"];$data = 0;// if file already exists, its initial size can be bigger than the new one, so we need to ignore it$wasLess = $lastId != 0;while ($data < $_GET["filesize"] || !$wasLess) {    // system calls are expensive and are being cached with assumption that in most cases file stats do not change often    // so we clear cache to get most up to date data    clearstatcache(true, $upload);    $data = filesize($upload);    $wasLess |= $data <  $_GET["filesize"];    // don't send stale filesize    if ($wasLess) {        sendMessage($lastId, $data);        $lastId++;    }    // not necessary here, though without thousands of `message` events will be dispatched    //sleep(1);    // millions on poor connection and large files. 1 second might be too much, but 50 messages a second must be okay    usleep(20000);}function sendMessage($id, $data){    echo "id: $idn";    echo "data: $datann";    ob_flush();    // no need to flush(). It adds content length of the chunk to the stream    // flush();}

注意事项:

安全。我是说运气据我了解,这是概念的证明,而安全是最不用担心的,但免责声明应该存在。这种方法从根本上来说是有缺陷的,仅当您不关心DOS攻击或有关文件的信息消失时才应使用此方法。

中央处理器。没有

usleep
该脚本将消耗100%的单个内核。长时间睡眠会使您有在单个迭代中上载整个文件的风险,并且永远不会满足退出条件。如果您在本地进行测试,
usleep
则应将其完全删除,因为在本地上传MB的时间约为毫秒。

打开连接。apache和nginx /
fpm都具有可以满足请求的php进程数量有限。上载单个文件所需的时间为2。在带宽缓慢或伪造请求的情况下,此时间可能会很长,并且Web服务器可能开始拒绝请求。

客户端部分。文件完全上载后,您需要分析响应并最终停止侦听事件。

编辑:

为了使它或多或少对生产友好,您将需要内存中的存储(例如redis或memcache)来存储文件元数据。

发出发布请求,添加一个唯一的令牌,该令牌标识文件和文件大小。

在您的Javascript中:

const fileId = Math.random().toString(36).substr(2); // or anything more unique...const [request, source] = [    new Request(`${url}?fileId=${fileId}&size=${filesize}`, {        method:"POST", headers:headers, body:file    })    , new EventSource(`${stream}?fileId=${fileId}`)];....

在data.php中注册令牌并按块报告进度:

....$fileId = $_GET['fileId'];$fileSize = $_GET['size'];setUnique($fileId, 0, $fileSize);while ($uploaded = stream_copy_to_stream($input, $file, 1024)) {    updateProgress($id, $uploaded);}....function setUnique($id, $size) {    // implement with your storage of choice}function updateProgress($id, $processed) {    // implement with your storage of choice}

因此,您的stream.php根本不需要打入磁盘,并且只要UX可以接受就可以hibernate:

....list($progress, $size) = getProgress('non_existing_key_to_init_default_values');$lastId = 0;while ($progress < $size) {    list($progress, $size) = getProgress($_GET["fileId"]);    sendMessage($lastId, $progress);    $lastId++;    sleep(1);}.....function getProgress($id) {    // implement with your storage of choice}

除非您放弃EventSource进行旧的良好拉拔,否则无法解决2个开放连接的问题。没有循环的stream.php的响应时间只有几毫秒,并且保持连接一直处于打开状态非常浪费,除非您每秒需要数百次更新。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5602202.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存