前端上传文件的过程中,往往会遇到大文件的情况,这时,若直接进行上传,则请求传输数据时间过长,往往超过网关设置的超时时间,从而导致文件上传失败,这个时候,就需要对大文件进行分片上传处理
前置条件:后端配合进行分片上传,提供分片上传接口,后端拼接文件进行存储
前置数据:file(大文件二进制数据file类型)、size(分片大小)
流程:
- 文件分片器:对file进行分片,得到片段队列chunkList,即任务队列
- 编写一个单片文件上传器:上传单片文件
- 编写任务调度器:从任务队列中取出文件片段fileChunk,调取第二步的单片文件上传器,记录上传结果。对于上传失败的片段进行重复上传,并记录重复上传次数;当重复上传次数大于预定次数时,暂停后续上传任务
- 启动文件分片器,分片器运行完毕后启动任务调度器
核心函数:
- 文件分片器
createFileChunk(file, size = 5 * 1024 * 1024) {
const fileChunkList = []
let count = 0
let index = 1
while (count < file.size) {
fileChunkList.push({
file: file.slice(count, count + size),
index,
})
index++
count += size
}
return fileChunkList
},
-
单片文件上传器 uploadChunkFile(chunk) 根据后端单片文件上传接口,编写即可,传入单片文件二进制数据
-
任务调度器 返回一个promise对象
sendRequest(fileChunkList) {
let finished = 0 //成功片数
const total = fileChunkList.length //总共分片数
const retryArr = [] //各片重传次数记录数组
const chunkRetry = 3 //重传预设次数
const errorTag = false //单片上传失败:重传次数用尽,设为true,后续任务暂停
let etagList = [] //上传成功后,接口返回数据记录
return new Promise((resolve, reject) => {
const handel = () => {
if (fileChunkList.length && fileChunkList.length > 0) {
//任务出栈
const chunk = fileChunkList.shift()
const index = chunk.index
uploadChunkFile(chunk)
.then((res) => {
//片上传成功
etagList.push(res)
finished++
if (!errorTag) {
//有一项失败,则不用继续上传,暂停请求
handel()
}
})
.catch((e) => {
if (typeof retryArr[index] !== 'number') {
//初始化该块的重试次数0
retryArr[index] = 0
}
retryArr[index]++
if (retryArr[index] > chunkRetry) {
errorTag = true
reject('单片文件上传失败', retryArr)
return //不往下进行重传操作
}
//重传,加入队列
fileChunkList.push(chunk)
// 当前任务完成,开启下一个任务
handel()
})
}
// 全部片段上传成功,将返回数据记录数组抛出
if (finished >= total) {
resolve(etagList)
}
}
// 控制并发,同时进行3项片段上传任务
for (let i = 0; i < 3; i++) {
handel()
}
})
},