背景
”小王啊,上传文件怎么不太好使啊?"
一个平淡无奇的工作日,产品兼测试对我提出了灵魂拷问
"你干啥上传3个g的文件??????(怒气值 30)😢"
"我就想上传一个大的文件,为什么不行"
"我可以加一个文件限制 超过多少内存不允许上传 (怒气值 40)"
"不行,我现在就需要上传这个大文件"
秉承着能用嘴巴完成的工作 绝不额外多敲代码的原则 我继续诉说我不加的理由
"这种情况不会出现大文件上传的问题的(除非脑子有泡)(怒气值 50)😤"
"可是现在的问题是我上传不上去 很明显这是你代码问题"
"代码没问题 一开始没说要做大文件上传 前后端都没有做 现在要做的话 会影响进度 (怒气值 70)😤"
"不行,加班也得给我做出来这个 我现在要求做这个"
"明天再说这个吧 我先把今天进度赶赶 (怒气值 90)😡🤬🤬"
"扣你绩效!!!"
"我现在立马就做 (怒气值 0)🤡🤡🤡"
妥协
Vue2.6 原生html input标签
<!-- 最外层div添加遮罩层 添加elUI的 loading状态 属性参数 -->
<div v-loading="loading" :element-loading-text="'上传中' + uploadProgress + '%'" >
<input ref="upload-zone" type="file" @change="handleFileSelect" />
</div>
Vue2.6 data部分
file: null, //上传的文件
fileMD5: '', //文件的MD5
chunkSize: 1024 * 1024 * 10, // 10MB,每个分片大小
uploadProgress: 0, //进度条数值
totalChunks: 0, // 总分片次数
Vue2.6 methods 部分
async handleFileSelect(event) {
//这里可以写一些业务需求代码 不符合要求过滤 可以理解成上传前的钩子函数
if () {
return
}
this.uploadProgress = 0
this.file = await event.target.files[0];
this.startUpload()
}
async startUpload() {
//没有文件列表直接跳出
if (!this.file) {
return;
}
//开启loading加载
this.loading = true
//生成MD5 + 时间戳唯一标识
this.fileMD5 = await this.calculateMD5() + new Date().getTime();
//计算出分片上传次数
this.totalChunks = Math.ceil(this.file.size / this.chunkSize);
//循环上传分片
for (let i = 0; i < this.totalChunks; i++) {
const start = i * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
//计算出对应的每个分片文件
const chunk = this.file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('fileName', this.file.name);
formData.append('chunkIndex', i);
formData.append('totalChunks', this.totalChunks);
formData.append('fileUniqueId', this.fileMD5);
//.... 可以添加一些自定义的参数
formData.append('XXXX', 'XXX');
try {
const response = await Api(formData, {
//适用于发送请求时的上传过程钩子函数
onUploadProgress: progressEvent => {
//这里可以写自己的业务代码
this.uploadProgress = ((i * this.chunkSize) + progressEvent.loaded) / this.file.size * 100 > 100 ? 100 : Number((((i * this.chunkSize) + progressEvent.loaded) / this.file.size * 100).toFixed(2));
}
})
//?什么时候上传成功 和后端约定好或者自己根据index判断
if () {
//上传成功 去掉遮罩层
this.loading = false
}
} catch (error) {
//捕获错误 清除input文件列表、进度条、遮罩层
this.$refs['upload-zone'].value = ''
this.loading = false
this.uploadProgress = 0
return
}
}
}
//生成MD5 函数 记得引入包 import SparkMD5 from 'spark-md5';
async calculateMD5() {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
const spark = new SparkMD5.ArrayBuffer();
let offset = 0;
fileReader.onload = (event) => {
console.log(event.target.result)
spark.append(event.target.result);
offset += event.target.result.byteLength;
if (offset < this.file.size) {
this.loadNext(offset, fileReader);
} else {
resolve(spark.end());
}
};
this.loadNext(offset, fileReader);
});
},
loadNext(offset, fileReader) {
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
const chunk = blobSlice.call(this.file, offset, offset + this.chunkSize);
fileReader.readAsArrayBuffer(chunk);
},
后记
不得不说ai生成的代码真好使 根据实际业务改一改可以省下很多敲代码时间
还有就是
我有一个漂亮可爱的女儿🥰