单个大型文件上传 Vue2.6

49 阅读3分钟

背景

”小王啊,上传文件怎么不太好使啊?"

一个平淡无奇的工作日,产品兼测试对我提出了灵魂拷问

"你干啥上传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生成的代码真好使 根据实际业务改一改可以省下很多敲代码时间

还有就是

我有一个漂亮可爱的女儿🥰