切片上传&&断点续传
背景:如果上传太大的文件,比如一个视频1g 2g那么大,上传过程中可能会出链接现超时的情况,而且也会超过服务端允许上传文件的大小限制,所以解决这个问题我们可以将文件进行分片上传,每次只上传很小的一部分 比如2M。(注意,切片上传需后端合作,以下为前端处理方式)。
1、在上传事件里做个简单的拦截,调取服务端接口获取一个上传这个文件的id,后边上传每个切片都需要将这个id传给服务端,切片合并需要用到。
getUploadIfon(file) {
//file当前文件
let param = {
contentLength: file.size, //文件大小
contentName: file.name, //文件名称
contentType: file.type, //文件类型
duration: Math.floor(Number(file.duration))
}
axios({
method: 'POST',
url: '/api',
data: param
}).then(({ data }) => {
if (data.code == '0000') {
this.uploadSlice(file, data.result)
}
})
},
2、文件分片
相信大家都对Blob 对象有所了解,它表示原始数据,也就是二进制数据,同时提供了对数据截取的方法slice,而 File 继承了Blob的功能,所以可以直接使用此方法对数据进行分段截图。切片数据根据服务端要求封装。
async uploadChunks(file, uploadId) { //file文件, uploadId,上传id合并文件需要用到
this.fileObj[file.uid] = [] //当前切片文件集合
const mN = file.size //文件大小
const fN = 2 * 1024 * 1000 //切片大小
let sN = 0
let partNumber = 0 //当前片数
while (sN < mN) {
partNumber++
var fileData = file.slice(sN, sN + fN)
this.fileObj[file.uid].push({
flie: fileData,
partNumber: partNumber,
uploadId: uploadId,
uid: file.uid
})
sN += fN
}
await this.sendRequest(this.fileObj[file.uid])
},
3、断点续传
如果文件太大,视频分割份数太多,可能部分切片上传失败。
// 断点续传&&并发处理
sendRequest(forms) {
var tempthreads = 3 //上传个数
var finished = 0 //接口成功数量
const total = forms.length //切片数量
const retryArr = [] // 数组存储每个文件hash请求的重试次数,做累加 比如[1,0,2],就是第0个文件切片报错1次,第2个报错2次
return new Promise((resolve, reject) => {
const handler = () => {
if (forms.length) {
// 出栈
const formInfo = forms.shift() //从第一个截取
const index = formInfo.partNumber //文件当前位置
// 数据封装
const fromData = new FormData()
fromData.append('file', formInfo.flie)
axios({
method: 'POST',
url:'/api',
data: fromData,
timeout: 3 * 60 * 1000,
}).then(({ data }) => {
if (data.code == '0000') {
finished++
handler()
} else {
this.updateStatus(formInfo, '上传失败')
// 抛错进入 catch 触发错误重试逻辑
throw new Error()
}
}).catch(e => {
// 累加错误次数
retryArr[index] == undefined
? (retryArr[index] = 1)
: retryArr[index]++
// 重试3次
if (retryArr[index] >= 3) {
this.updateStatus(formInfo, '上传失败')
return reject('重试失败', retryArr)
}
// 将失败的重新加入队列
forms.push(formInfo)
handler()
})
}
if (finished >= total) {
//如果成功数量和切片数量一致,停止
resolve('done')
}
}
// 控制并发
for (let i = 0; i < tempThreads; i++) {
handler()
}
})
},
所有切片上传完成后,再向后端发送一个上传完成的请求,即通知后端把所有切片进行合并,最终完成整个上传流程。
大功告成!本文主要讲了前端的实现思路,最终落地成完整的项目,还是需要大家根据真实的项目需求来实现。