1.大文件上传的解决思路
1.文件切片:把一个大文件转换成二进制内容,然后按照一个固定的大小对二进制内容进行切割,得到多个小文件,然后循环上传所有的小文件。在js中,文件File对象是Blob对象的子类,可以使用slice()方法完成对文件的切割;
2.文件合并:当所有小文件上传完成,调用接口通知后端把所有的文件按编号进行合并,组成大文件;
3.并发控制:结合Promise.race和异步函数实现,限制多个请求同时并发的数量,防止浏览器内存溢出;
4.断点续传:把所有上传失败的小文件加入一个数组里面,在所有小文件都上传结束(成功和失败都算结束)之后再上传一次上传失败了的小文件,反复执行这一步,直到所有小文件都上传成功,可以通过递归实现。
2.代码实现
<input type="file" id="fileInput">
<button id="uploadBtn">上传</button>
<script>
// 设置基地址
axios.defaults.baseURL = 'http://localhost:3000'
let file = null
// 获取文件
document.querySelector('#fileInput').addEventListener('change', e => {
file = e.target.files[0]
})
document.querySelector('#uploadBtn').addEventListener('click', () => {
if (!file) return
// 文件分片
let hash = 0 // 切片序号
let size = 1024 * 50 // 切片大小
let fileArr = []
for (let i = 0; i < file.size; i = i + size) {
fileArr.push({
hash: hash++,
chunk: file.slice(i, i + size)
})
}
// 上传文件,并发控制和断点续传
const uploadFileChunks = async (list) => {
if (list.length === 0) {
await axios({
method: 'get',
url: '/merge',
params: {
filename: file.name
}
})
console.log('上传完成')
return
}
let pool = [] // 并发池
const max = 3 // 并发池异步操作的数量
let failList = [] // 上传失败的文件列表
let finish = 0 // 上传完成的数量
// 遍历文件列表,上传文件
for (let i = 0; i < list.length; i++) {
let item = list[i]
console.log(item)
let formData = new FormData()
formData.append('filename', file.name)
formData.append('hash', item.hash)
formData.append('chunk', item.chunk)
// 上传
let res = axios({
method: 'post',
url: '/upload',
data: formData
})
// 把上传文件的异步操作放入并发池里
pool.push(res)
if (pool.length === max) {
// 每当并发池跑完一个任务,就再塞入一个任务
await Promise.race(pool)
}
res.then(() => {
// 请求成功,从并发池里移除
const index = pool.findIndex(it => it === res)
pool.splice(index, 1)
}).catch(() => {
// 请求失败,从并发池里移除,添加到失败的文件列表
const index = pool.findIndex(it => it === res)
pool.splice(index, 1)
failList.push(item)
}).finally(() => {
finish++
// 如果请求都完成了,递归调用自己,把上传失败的文件列表再上传一次
if (finish === list.length) {
uploadFileChunks(failList)
}
})
}
}
uploadFileChunks(fileArr)
})
</script>
3.小结
上面的代码实现只是一个很简单的大文件上传功能,有很多问题都没有解决,只是提供了一个实现大文件上传的思路。
在工作中要实现大文件上传的话推荐使用目前社区已经存在的一些成熟的大文件上传解决方案,如七牛SDK,腾讯云SDK等