问题描述:
在上传大文件的时候,有时候太大或者网络不好,用户可能会等待太久而不知道上传了多少。
解决思路:
使用异步返回一个文件上传返回的进度数,再添加UI显示到页面上。
优化:
大文件上传使用异步分包处理,加快文件上传的速度。
中途问题:
刚开始使用原生js,采用XMLHttpRequest、fetch方法。
XMLHttpRequest方法一直堵塞,需要一个包上传完成之后才可以上传第二个包。
fetch方法使用保存fetch的返回结果的Promise到一个Promise数组uploadPromises中,最后再使用await Promise.all(uploadPromises)来等待全部块上传完成之后进行返回上传成功的结果,但是同样的,也会被堵塞。
查询资料了解到:使用原生的 XMLHttpRequest 或者 fetch API,很难实现真正的并行上传,因为浏览器的限制会导致请求在某些情况下被阻塞。这可能会导致虽然同时创建了多个上传请求,但它们实际上可能会被串行处理。
代码思路:
考虑使用 Web Workers、现代的上传库(如 axios 或 fetch-with-progress)、或者其他基于 XMLHttpRequest 的库,以实现并行上传的功能,这里我使用的是axios去处理。
axios方法
async function uploadFile(file){
// 初始化定义
const chunkSize = 1024 * 1024 * 5; // 每个块的大小(这里设置为5MB)
const totalChunks = Math.ceil(file.size / chunkSize); // 总块数
const uploadProgress = []; // 存放各分块上传进度的数组,用于求平均值
// 创建并发上传任务
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const start = chunkIndex * chunkSize; // 起始位置
const end = Math.min(start + chunkSize, file.size); // 结束位置,判断是否大于总文件大小,拿较小的一个
const chunk = file.slice(start, end); // 分割的块
const chunkName = `${file.name}.part${chunkIndex}` // 设置的块名,方便后端区分是哪一个块
// 封装数据,使用append方法添加传递给后端的信息
const formData = new FormData();
formData.append("file", chunk, chunkName); // 块文件
formData.append("index", chunkIndex) // 块索引
formData.append("total", totalChunks) // 总块数
formData.append("fileName", file.name); // 上传文件名
const uploadPromise = axios.post("你的上传接口", formData, {
// axios配置项,监听上传进度
onUploadProgress: progressEvent => {
const percentComplete = (progressEvent.loaded / progressEvent.total) * 100; // 当前块的上传百分比
uploadProgress[chunkIndex] = percentComplete; // 将当前块的百分比更新到数组中
const percent = calculateAverage(uploadProgress); // 计算百分比平均值
// 此处调用修改进度条UI的数值
// ......
}
}).then(res => { // 此处res为后端传递返回的数据
if(res.status === 200){ // 若状态码成功
if(res.finished){ // 判断是否已经全部完成
// 已经全部传输完毕的操作
// ......
}
}else{
// 某个块chunkIndex上传出错
}
}).catch(error => {
console.error(`块 ${chunkIndex + 1} 上传出错:`, error);
});
}
}
// 求数组中的平均数
function calculateAverage(numbers) {
let sum = 0;
numbers.forEach(num => {
sum += num;
});
return sum / numbers.length;
}
总结
1、使用分包上传返回进度首先要分块,使用for循环将每次请求块的起始和终止位置算出,使用
Formdata()将块数据封装起来。
2、使用axios接口上传,并配置一个onUploadProgress属性来监听返回的上传进度信息,使用数组存储各进度信息,计算出平均值,进行返回或者设置外部进度条UI的样式,并使用then监听是否已经上传完成。