文件(分片)并行上传时计算总的上传进度
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 值,供下一次回调使用。
需要记录每个文件上次已上传的字节数
适用于文件大小差异大的场景