1. 基于已上传字节数计算
<template>
<input type="file" multiple @change="uploadFiles" />
<div>总进度:{{ totalProgress }}%</div>
</template>
<script>
export default {
data() {
return {
totalProgress: 0,
filesProgress: {}, // 存储每个文件的上传进度 { filename: percentage }
}
},
methods: {
async uploadFiles(e) {
const files = e.target.files;
if (!files.length) return;
// 初始化进度记录
this.filesProgress = {};
Array.from(files).forEach(file => {
this.filesProgress[file.name] = 0;
});
// 并行上传
const promises = Array.from(files).map(file => {
const formData = new FormData();
formData.append('files', file);
return axios.post('/upload', formData, {
onUploadProgress: (progressEvent) => {
// 更新单个文件进度
this.filesProgress[file.name] = Math.round(
(progressEvent.loaded / progressEvent.total) * 100
);
// 计算总进度
this.calculateTotalProgress();
}
});
});
await Promise.all(promises);
},
calculateTotalProgress() {
const progresses = Object.values(this.filesProgress);
const total = progresses.reduce((sum, p) => sum + p, 0);
this.totalProgress = Math.round(total / progresses.length);
}
}
}
</script>
原理:
为每个文件维护独立的进度(0-100)
实时计算所有文件进度的平均值
2. 基于已上传文件数计算(适合等大小文件)
data() {
return {
uploadedCount: 0,
totalFiles: 0
}
},
methods: {
async uploadFiles(e) {
const files = e.target.files;
this.totalFiles = files.length;
this.uploadedCount = 0;
await Promise.all(files.map(file => {
return axios.post('/upload', file)
.then(() => {
this.uploadedCount++;
this.totalProgress = Math.round(
(this.uploadedCount / this.totalFiles) * 100
);
});
}));
}
}
适用场景:当所有文件大小相近时更高效,实际计算的上传文件数占总数的比
3. 精确字节级计算
data() {
return {
totalSize: 0, 总字节数
prevLoadedMap: {}, // 存储 { 文件名: 上次loaded值 }
uploadedSize: 0, // 总已上传字节数
}
},
methods: {
getPrevLoaded(filename) {
return this.prevLoadedMap[filename] || 0;
},
savePrevLoaded(filename, loaded) {
this.prevLoadedMap[filename] = loaded;
},
async uploadFiles(e) {
const files = e.target.files;
// 计算总大小
this.totalSize = Array.from(files).reduce((sum, file) => sum + file.size, 0);
this.uploadedSize = 0;
const promises = files.map(file => {
const formData = new FormData();
formData.append('file', file);
return axios.post('/upload', formData, {
onUploadProgress: (progressEvent) => {
// 累加已上传字节数
this.uploadedSize += progressEvent.loaded - this.getPrevLoaded(file.name);
this.savePrevLoaded(file.name, progressEvent.loaded);
this.totalProgress = Math.round(
(this.uploadedSize / this.totalSize) * 100
);
}
});
});
await Promise.all(promises);
}
}
- progressEvent.loaded 的特性
progressEvent.loaded 表示 当前文件已上传的字节数。
在文件上传过程中,这个值会通过多次回调递增(例如从 0 → 500KB → 1MB → 完整文件大小)。
问题:如果直接累加 progressEvent.loaded,会导致重复计算(例如第一次+500KB,第二次+1MB,实际只新增了500KB,但会错误累加1MB)。- 解决重复累加的关键
this.getPrevLoaded(file.name)
获取该文件上一次回调时的 loaded 值(初始为0)。
progressEvent.loaded - this.getPrevLoaded(file.name)
计算出本次回调新增的字节数(正确的增量)。
this.savePrevLoaded(file.name, progressEvent.loaded)
保存当前 loaded 值,供下一次回调使用。
需要记录每个文件上次已上传的字节数
适用于文件大小差异大的场景