大文件上传
- 对文件做切片:将一个请求拆分成多个请求,每个请求的时间机会缩短,且如果某个请求失败,只需要重新发送这一次请求即可,无需重头开始;
- 通知服务器合并切片:在上传完所有切片后,前端发送情趣通知服务器做切片处理;
- 控制多个请求的并发量:防止多个请求同事发送,造成浏览器内存溢出,导致页面卡死;
- 断点续传:当多个请求中有请求失败,例如出现网络故障,页面关闭等,我们要对失败的请求做处理,让它们重新发送;
- 文件切片:
原理:文件FIle对象是Blob对象的子类,Blob对象包含一个重要的方法slice(),通过这个方法我们就可以对二进制文件进行拆分;
// 创建切片
let size = 1024 * 50
let fileChunks = []
let index = 0 //切片序号
for(let cur = 0
fileChunks.push({
hash: index++,
chunk: file.slice(cur, cur + size)
})
}
- 并发控制:
结合Promise.race和异步函数实现多个请求同时并发的数量
- 断点续传:
单个请求失败后,触发catch方法的时候,把当前请求用一个失败列表存放起来,在本轮所有请求完成后,重复对失败请求做处理
const uploadFileChunks = async function(list){
if(list.length === 0){
await axios({
method: 'get',
url: '/merge',
params: {
filename: file.name
}
});
console.log('上传完成')
return
}
let pool = []
let max = 3
let finish = 0
let failList = []
for(let i=0;i<list.length;i++){
let item = list[i]
let formData = new FormData()
formData.append('filename', file.name)
formData.append('hash', item.hash)
formData.append('chunk', item.chunk)
let task = axios({
method: 'post',
url: '/upload',
data: formData
})
task.then((data)=>{
let index = pool.findIndex(t=> t===task)
pool.splice(index)
}).catch(()=>{
failList.push(item)
}).finally(()=>{
finish++
if(finish===list.length){
uploadFileChunks(failList)
}
})
pool.push(task)
if(pool.length === max){
await Promise.race(pool)
}
}
}