大文件上传

282 阅读2分钟

切片上传&&断点续传

背景:如果上传太大的文件,比如一个视频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()  
}  
})  
},  

所有切片上传完成后,再向后端发送一个上传完成的请求,即通知后端把所有切片进行合并,最终完成整个上传流程。

大功告成!本文主要讲了前端的实现思路,最终落地成完整的项目,还是需要大家根据真实的项目需求来实现。