您的位置 首页 php

前端websocket分块/分段上传多个大文件

分块上传多个大文件,这里记录一下我的开发历程。

前端用 websocket , websocket server用的是php的swoole扩展。

几经探索,发现 简单、靠谱、占内存少 的大致流程是这样的:

  1. 读取数据块->发送数据块->等待服务端保存完毕->读取下一个数据块->发送数据块->等待服务端保存完毕->…
  2. 文件a, 读取最后一个数据块->发送数据块->等待服务端保存完毕。
  3. 承上启下的重头戏:
  • 浏览器端, 文件b, 读取第1个数据块->发送数据块->等待服务端保存完毕->读取下一个数据块->发送数据块->等待服务端保存完毕->…
  • 服务端,worker主进程派发任务给taskWorker进程,由该进程将文件a的各个数据块按顺序合并为大文件。

每一个文件的准备工作【读取数据块之前】:

  1. 算出:需要切成几块;
  2. 算出:每一块的大小及其序号;
  3. 加上文件名、文件大小、文件类型,以json格式一并发给服务端,这个是为了
  • 保存的是第几块的数据,还多少M需要上传;
  • 接收到的二进制数据,是否跟当时读取的大小一样,万一,由于网络原因,没有传全,可以丢弃,避免文件“变质”

服务端保存完这些json数据,告诉浏览器, “hi, 我保存好啦,你可以开始发送这个文件的数据块啦”,浏览器才开始吭吭哧哧地读取第1个文件的第1个数据块。

服务端保存了这个数据块成功了,更新redis中的信息,包括:

  • 已上传的大小,
  • 剩余大小
  • 下一个数据块的序号
  • 小文件的路径+文件名(如果是放在磁盘中)或者文件的fileId(如果用的MongoDB gridFs), 这是日后合并小文件的依据。
  • 这个文件是否传了全部的数据块, isFinish,
$isFinish = $fileSize === $totalUploadSize; 
 

如果为false, 则:

  • 告诉浏览器, :(例句)”hi, 我保存的是第20块的数据,还有57M需要上传”。
  • 然后,浏览器开始读取这个文件的下一个数据块。

否则:

  • 告诉浏览器, :例句)”hi, 我保存的是第29块的数据,还有0Bit需要上传”。
  • 然后,浏览器开始欢天喜地读取下一个文件的第1个数据块。

为了测试分块上传后,文件是否还是原来的样子,我特意挑选视频(至少100M的那种)作为测试材料。

使用html api提供的 File reader 对象 , 读取文件的方法:

  • readAsText 方法 用于读取文本文件—限制了文件类型,没有尝试,直接放弃。
  • readAsDataURL 方法 返回一个data URL,它的作用基本上是将文件数据进行Base64编码。你可以将返回值设为图像的src属性。—-没有尝试,直接放弃
  • readAsBinaryString 方法 可以读取任意类型的文件,而不仅仅是文本文件。这个方法与XMLHttpRequest.sendAsBinary方法结合使用,就可以使用JavaScript上传任意文件到服务器。—–尝试了,踩到坑了。。。
  • readAsArrayBuffer 方法读取文件,返回一个类型化数组(ArrayBuffer),即固定长度的二进制缓存数据。—尝试了,切多少,读取到的数据就是多少,服务端按顺序合并为大文件后,体积不变,可以播放。

一开始,也不懂,跟着网上的大佬使用readAsArrayBuffer。

var reader = new FileReader();
var blob = reader.slice(startSize, endSize);
reader.readAsArrayBuffer(blob);
reader.onload = function(e) {
 var arrayBuffer = e.target.result;
 }
 
 

如上,读取是异步非阻塞的,好有诱惑力。

我禁不住诱惑,在循环计算每一个数据块的startSize和endSize时,就运行读取操作,由于读取是异步非阻塞的->读取数据块是争先恐后的->哪一块先读取成功是不确定的。

结果是,

  • 我使用谷歌浏览器开发者版,标签页还常常卡死,控制台中显示javascript占用了几百兆的内存!!!
  • 错上加错: 先发送json, 紧接着发送二进制数据,居然可以还原大文件,其实是因为,我的websocket server是以同步阻塞(即便使用协程也不过是半阻塞)的方式处理数据!

为了同时发json和文件数据,我尝试了readAsBinaryString….我发现,切 5M 的数据,读取到的数据不止5M。服务端按顺序合并为大文件后,体积大了好多,无法播放。。。唉,都是泪呀。

又辗转回readAsArrayBuffer。

下一篇说说, 如何用js获取文件的md5值 , 这样,可以避免上传名称不同、文件内容相同的文件。

关于 断点续传 ,我还在路上。。。

文章来源:智云一二三科技

文章标题:前端websocket分块/分段上传多个大文件

文章地址:https://www.zhihuclub.com/152754.shtml

关于作者: 智云科技

热门文章

网站地图