本文详解前端获取文件上传进度的多种方案。涵盖使用 Axios 的 onUploadProgress、原生 Ajax 的 XMLHttpRequest 事件以及 Fetch API 的 onprogress 回调。针对多文件上传,介绍统一进度条与独立进度条的实现逻辑。此外,深入解析大文件分片上传的优势、切片索引处理及断点续传机制,帮助开发者优化上传体验。
获取上传进度的几种方式
近期测试反馈指出,当前的文件上传等待界面缺乏动态加载效果,视觉体验不佳,需要优化。产品部门建议参考其他业务线的上传交互设计。起初认为仅需添加一个静态加载图标,但发现其他业务线采用了环形或线性的动态进度条。由于现有接口并未返回实时进度数据,且当前采用逐个文件分批次调用的方式,若仅依据上传完成的文件数量来估算进度,因文件大小差异较大,会导致进度显示极不准确。
随后与另一位前端同事交流,得知 Axios 提供了监听整个上传进度的 API。基于此信息,我对文件上传的进度监听机制进行了深入研究,并整理出以下总结。
前端使用axios实现监听上传进度
首先介绍如何使用 Axios 实现上传进度的监听,具体实现代码如下:
axios.post('/api/file/uploadfile', form, {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: (progressEvent) => {
if (progressEvent.lengthComputable) {
this.progresss = Math.round((progressEvent.loaded / progressEvent.total) * 100);
}
},
})
.then((res) => {
// ...
});前端使用ajax获取上传进度
接下来探讨如何利用原生 Ajax 获取上传进度,实现方式如下:
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const progressPercentage = Math.round((event.loaded / event.total) * 100);
console.log(`上传进度: ${progressPercentage}%`);
}
});前端使用fetch API获取上传进度
介绍如何使用 Fetch API 获取上传进度,具体代码如下:
const file = document.getElementById('fileInput').files[0];
fetch('/upload', {
method: 'POST',
body: file,
onprogress: (event) => {
if (event.lengthComputable) {
const progressPercentage = Math.round((event.loaded / event.total) * 100);
console.log(`上传进度: ${progressPercentage}%`);
}
},
})
.then((response) => {
// 处理响应结果
})
.catch((error) => {
// 处理错误
});上述几种方法主要用于展示单一的上传进度条。那么,还有哪些其他方式来展示进度条呢?
多文件上传展示一个进度条
如果系统支持多文件上传,并通过文件数量来展示进度条,通常该如何实现?
假设需要上传 10 个文件,在上传第一个文件时,进度条应控制在 10% 以下,后续文件依此类推。具体实现代码如下:
let progress = 0;
const UPLOAD_TYPE_MAP = {
UPLOADING: 'uploading',
COMPLATE: 'complate',
}
const files = [{ file:'', status: UPLOAD_TYPE_MAP.UPLOADING }, { file:'', status: UPLOAD_TYPE_MAP.UPLOADING }];
for (let index = 0; index < files.length; index++) {
const element = files[index];
// 1、这里是调接口的逻辑,可以是并行也可以完成一个调下一个,这个看你们的接口的限速吧,我就按照并行做了。
// 2、这里是每个文件上传完成的逻辑,更改当前文件的状态把status 改成UPLOAD_TYPE_MAP.COMPLATE
}
const timer = setInterval(() => {
const eachProgress = 100 / files.length; // 计算单个的比例
const complateUpload = files.filter((item) => item.status === UPLOAD_TYPE_MAP.COMPLATE);
const upperLimit = (complateUpload.length + 1) * eachProgress;
const maxProgress = upperLimit > 100 ? 100 : upperLimit; // 计算出当前最大进度
if(progress < maxProgress) {
progress++; // 边界控制
}
if(maxProgress === 100) {
progress = 100;
clearInterval(time);
}
}, 500);以上展示了多文件共用一个进度条的具体代码。需要注意的是,这种方法并不完全准确,因为文件大小存在差异。如果文件大小相近,该方案尚可接受。实际上,最佳方案是利用 Axios、Ajax 等技术分别为每个文件展示独立的进度条,具体选择需视业务需求而定。
大文件分片上传展示进度条
首先简要说明分片上传的优势,具体如下:
- 可靠性和鲁棒性:将大文件分割为小块上传,即使发生网络中断或意外情况,也只需重新上传失败的分片,无需重新上传整个文件,从而提高了上传的可靠性和鲁棒性。
- 传输效率:大文件被分割为多个小块并行上传,可以充分利用网络带宽,从而提高传输速度和效率。
- 断点续传:如果上传过程中断,可以根据已成功上传的分片信息,从上次中断的地方继续上传,避免重复上传已上传的部分,节省时间和资源。
- 内存占用低:一次性将大文件加载到内存可能导致内存溢出,而分片上传只需加载单个分片,减少了对内存的压力,适应内存有限的环境。
- 服务器负载平衡:使用分片上传可以将上传请求分散到多个服务器,降低单一服务器的负载压力,提高整体系统的稳定性和吞吐量。
- 灵活性:分片上传允许用户在任意时间暂停、取消或恢复上传过程,提供了更好的用户体验和操作灵活性。
常有人疑惑,为何其他网站上传速度很快,而自己的网速相同却较慢。这往往与是否进行分片上传有关。虽然直接上传一个文件开发更快,但分片上传会增加前端和服务端的工作成本。接下来,我们将探讨如何实现分片上传:
// 定义分片大小(以字节为单位) // 1MB
// 将文件切割为多个分片
function sliceFileIntoChunks(file, chunkSize = 1024 * 1024) {
const fileSize = file.size;
const chunks = Math.ceil(fileSize / chunkSize);
const fileChunks = [];
let start = 0;
let end = chunkSize;
for (let i = 0; i < chunks; i++) {
const chunk = file.slice(start, end); // slice 可以把文件按照需要的大小分割
fileChunks.push(chunk);
start = end;
end = start + chunkSize;
}
return fileChunks;
}获取上传进度(此需求回到了【多文件上传展示一个进度条】的主题),具体实现如下:
const files = sliceFileIntoChunks(originFile).map((item) => ({file: item, status: UPLOAD_TYPE_MAP.UPLOADING}));
// ... 忽略
const timer = setInterval(() => {
const eachProgress = 100 / files.length; // 计算单个的比例
const complateUpload = files.filter((item) => item.status === UPLOAD_TYPE_MAP.COMPLATE);
const upperLimit = (complateUpload.length + 1) * eachProgress;
const maxProgress = upperLimit > 100 ? 100 : upperLimit; // 计算出当前最大进度
if(progress < maxProgress) {
progress++; // 边界控制
}
if(maxProgress === 100) {
progress = 100;
clearInterval(time);
}
}, 500);需要注意一点,必须告知服务端切片索引,以便服务端后续合并切片。如果按大小(如 1MB)切割,上传完成后需发送通知告知服务端合并切片。如果按份额(如 10 等份)切割,上传完成的请求中无需额外通知服务端,服务端也可判断上传状态。
结语
至此,关于 JavaScript 获取上传进度的几种方式的文章介绍完毕。更多相关 JavaScript 获取上传进度的内容,请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!