大文件续传
是什么
针对问题:
在同一个请求中,要上传大量的数据,导致整个过程会比较漫长,且失败后需要重头开始上传。解决思路:
将这个请求拆分成多个请求,每个请求的时间就会缩短,且如果某个请求失败,只需要重新发送这一次请求即可,无需从头开始,这样是否可以解决大文件上传的问题呢?要求
- 支持拆分上传文件请求
- 支持断点续传
- 支持显示上传进度和暂定进度
技术
- webWorker:
是一种在后台线程中运行脚本的技术,允许开发者在不阻塞用户界面的情况下执行复杂和耗时的任务。Web Worker 提供了一个独立的执行环境,与主线程(UI 线程)隔离开来,避免了长时间运行的脚本导致的页面卡顿。
- webWorker:
逻辑
- 1、将上传的文件切片,并对每个切片标上记号,是哪个文件的切片是切片的那一部分(用hash实现标记)
- 2、后端把成功的标记记录下来,上传每个文件时,都判断一下标记是否存在,如果存在不上传,如果存在就上传。
- 3、后端对上传完整的文件切片进行拼接。
细节
webworker 处理对文件的切片
webWorker的特点
- 独立线程:Web Worker 在独立的线程中运行,与主线程并行执行。
- 无阻塞:由于在独立线程中运行,Web Worker 不会阻塞主线程的执行。
- 通信机制:主线程和 Worker 线程之间通过消息传递(postMessage 和 onmessage)进行通信。
- 受限环境:Worker 线程不能访问 DOM,也不能调用一些特定的 Web API,如 alert 和 localStorage。
基本用法
- 监听消息事件
1
2
3
4
5
6self.addEventListener('message', async e => {
console.log(e)
const { file, chunkSize } = e.data
const fileChunkList = await createFileChunk(file, chunkSize)
await calculateChunksHash(fileChunkList)
}) - 监听错误事件
1
2
3
4
5self.addEventListener('error', function (e) {
console.log("%cWorker 线程 error 监听事件: ", 'color: red', e)
// worker 线程关闭
self.close()
})
切片实现
- 1、文件切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14//第一个参数是file,第二个是切片大小
function createFileChunk(file, chunkSize) {
// new Promise 的基本用法:来创建一个新的 Promise 对象,该函数有两个参数:resolve 和 reject。
//resolve 用于在异步操作成功时返回结果,reject 用于在异步操作失败时返回错误。
return new Promise(resolve => {
let fileChunkList = []
let cur = 0
while (cur < file.size) {
fileChunkList.push({ chunkFile: file.slice(cur, cur + chunkSize) })
cur += chunkSize
}
resolve(fileChunkList)
})
}
切片标记
- 每个切片有自己的MD5哈希值,所有切片的哈希值保存在SparkMD5.ArrayBuffer 实例spark中,在切片完成后,spark值传递给fileHash,webWork将fileHash和fileChunkList文件切片传给了主线程。
1 | // 记载并计算文件切片的 md5 |
主线程任务
- 该方法能依次上传多个文件。
- 切片大小设置为5MB
- 多个文件输入处理逻辑:
- 如果输入框有change事件,判断是否有文件传入,如果有将每个文件进行切片处理,并上传
- 对每个文件绑定响应式对象,对象包括文件名、文件大小、文件状态、所有需要上传的切片、文件hash、最大报错次数、上传进度等属性。
- 根据文件的状态来确定,当前文件需要进行的流程。
- 需要切片上传的文件,交给webworker切片,并将结果返回。作为参数传给开始上传函数
- 多个文件并行上传
- 开始上传文件的逻辑:
- 需要参数,file,inTaskArrItem(初始化的一个对象,包含单个文件的属性), fileChunkList(单个文件的切片)
- 判断文件是否已经存在于服务器,如果存在直接return,如果不存在执行上传的逻辑
- 将每个切片的信息写入inTaskArrItem的allChunList(所有需要上传的切片)中
- 对allChunkList进行过滤,过滤掉已经上传成功的切片(判断是否上传成功,由checkfile函数完成)
- 如果没有需要上传的切片,但是前面判断服务器中没有此文件,说明需要合并,再次执行文件合并函数
- 每一个文件的处理逻辑
- 如果没有需要上传的切片或者状态为正在被上传,不做处理
- 找出需要上传的文件,并将入待处理文件的列表中。根据列表长度计算每个文件的并发请求数量。(chrome浏览器同域名同一时间请求的最大并发数限制为 6,如果有三个文件需要上传,那么每个文件上传只能同时并发2个请求)
- 从需要上传的切片尾部拿2个切片,放入请求数组中。并将其从allChunkList中删除
- 开始上传切片
- 切片上传逻辑
- 首先判断文件状态,如果文件状态为暂停或终端就什么都不错
- 每个切片由三次机会,如果三次都失败,则文件上传失败