react+vue通用的前端大文件的分片、秒传、断点续传、md5抽片计算

大文件上传

用于大文件的分片上传,秒传,断点续传,多线程上传。
md5的抽片生成或者md5全文件内容生成

  1. 使用原生input进行上传–在vue2中使用的例子

    accept限制选中文件的格式

    <label class="file-label" style="cursor:pointer;">
         上传资料
         <input style="display:none"  accept="image/bmp, image/jpeg, image/png, audio/mpeg, video/mp4, application/x-zip-     compressed, text/plain, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf"
         ref="input_file" class="input_file" id="File" type="file" @change='upload' />
    </label>
    
  2. 上传校验文件类型或大小

    //上传文件
            async upload(e) {
                let fileAccept = "image/bmp, image/jpeg, image/png, audio/mpeg, video/mp4, application/x-zip-compressed, text/plain, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf"
                if (fileAccept.indexOf(e.target.files[0].type) === -1) {
                    Message.error('非格式类型文件,请重新上传!')
                    return;
                }
                const file = e.target.files[0];
                if (!file) return;
                if (event[0].size > 5 * 1024 * 1024 * 1024) {
                	Message.warning("单个文件上传大小已超出最大限制(5GB)")
                	return
                }
                let fileMd5 = await this.calcFileMD5(file)
                let result = await this.$mApi.getAttachmentState(fileMd5)
                //文件已上传
                if (result.data.state == 'EXIST') {
                    this.uploadDocument(result, file.name, fileMd5)
                } else {
                    this.uploadSingle(file, fileMd5, result, '正在上传资料', 3);
                }
            },
    
  3. 文件生成md5-若为zip则使用抽片计算,小于20片就全部计算其余文件全部计算

    //导入计算文件MD5
            calcFileMD5(file) {
            //判断哪一种要抽片生成md5
                if (file.type == 'application/zip' || file.type == 'application/x-zip-compressed') {
                    return new Promise((resolve, reject) => {
    
                    let chunkSize = 1024 * 1024 * 20, // 20M
                        chunks = Math.ceil(file.size / chunkSize),
                        currentChunk = 0,
                        currentChunkNum = 0,
                        spark = new SparkMD5.ArrayBuffer(),
                        fileReader = new FileReader();
                    this.loading = this.$loading({ text: '正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%' });
    
                    let blocks = 20
                    let jumpNum =  Math.ceil(chunks/blocks)
                    console.log(chunks,'chunks',jumpNum)
                    // 计算抽样的起始位置和数量
                    let sampleStart = chunkSize * 2 , // 开始
                        sampleEnd = chunkSize * (chunks - 2), //结束
                        sampleChunks = Math.ceil((sampleEnd - sampleStart) / chunkSize); // 取样块数
    
                    fileReader.onload = (e) => {
                        spark.append(e.target.result);
                        currentChunkNum += jumpNum;
                        currentChunk ++;
    
                        // 如果是抽样的片段,则计算抽样的 MD5
                        if (currentChunk >= blocks && currentChunk < chunks - 2 && (currentChunk - 2) % Math.ceil(chunks / sampleChunks) === 0) {
                            let sampleSpark = new SparkMD5.ArrayBuffer();
                            sampleSpark.append(e.target.result);
                            resolve(sampleSpark.end());
                            this.loading.close();
                            return;
                        }
                        console.log(currentChunk,'currentChunk',chunks)
                        if (currentChunk < chunks) {
                            console.log(1)
                            this.loading.setText('正在校验文件:' + (currentChunk / (blocks>chunks?chunks:blocks) * 100).toFixed(2) + '%')
                            loadNext();
                        } else {
                            resolve(spark.end());
                            this.loading.close()
                        }
                    };
    
                    fileReader.onerror = (e) => {
                        reject(fileReader.error);
                        reader.abort();
                    };
    
                    function loadNext() {
                        let start, end;
                        //抽片的逻辑
                        start = currentChunkNum * chunkSize;
                        end = start + chunkSize >= file.size ? file.size : start + chunkSize;
                        fileReader.readAsArrayBuffer(file.slice(start, end));
                    }
                    loadNext();
                });
                } else {
                    // 不是zip类型的压缩包
                    return new Promise((resolve, reject) => {
                    let chunkSize = 1024 * 1024 * 20, // 20M
                        chunks = Math.ceil(file.size / chunkSize),
                        currentChunk = 0,
                        spark = new SparkMD5.ArrayBuffer(),
                        fileReader = new FileReader();
                    this.loading = this.$loading({ text: '正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%' });
                    fileReader.onload = (e) => {
                        spark.append(e.target.result);
                        currentChunk++;
                        if (currentChunk < chunks) {
                            console.log(1)
                            this.loading.setText('正在校验文件:' + (currentChunk / chunks * 100).toFixed(2) + '%')
                            loadNext();
                        } else {
                            resolve(spark.end());
                            this.loading.close()
                        }
                    };
    
                    fileReader.onerror = (e) => {
                        reject(fileReader.error);
                        reader.abort();
                    };
    
                    function loadNext() {
                        let start = currentChunk * chunkSize,
                            end = start + chunkSize >= file.size ? file.size : start + chunkSize;
                        fileReader.readAsArrayBuffer(file.slice(start, end));
                    }
                    loadNext();
                });
                }
    
            },
    
  4. 多线程上传

    //多线程上传
            async asyncPool(poolLimit, array, iteratorFn) {
                const ret = []; // 存储所有的异步任务
                const executing = []; // 存储正在执行的异步任务
                for (let index = 0; index < array.length; index++) {
                    // 调用iteratorFn函数创建异步任务
                    const p = Promise.resolve().then(() => iteratorFn(array[index], index));
                    ret.push(p); // 保存新的异步任务
                    // 当poolLimit值小于或等于总任务个数时,进行并发控制
                    if (poolLimit <= array.length) {
                        // 当任务完成后,从正在执行的任务数组中移除已完成的任务
                        const e = p.then(() => executing.splice(executing.indexOf(e), 1));
                        executing.push(e); // 保存正在执行的异步任务
                        if (executing.length >= poolLimit) {
                            await Promise.race(executing); // 等待较快的任务执行完成
                        }
                    }
                }
                return Promise.all(ret);
            },
    
  5. 上传文件信息

    // 文件上传信息
            async uploadSingle(file, fileMd5, result, prompt, promptType) {
                if (!file) return;
                console.log(file, 'file')
                //所有文件块
                let ckunkFile = await this.splitFile(file)
                let totalSize = file.size; //文件大小
                let blockSize = 1024 * 1024 * 20; //块大小
                let chunk = 0; // 当前块数
                let chunks = Math.ceil(totalSize / blockSize)
                    ? Math.ceil(totalSize / blockSize)
                    : 1; //总块数
                console.log(chunks, '总块数', promptType)
                let type = 'course'
                //obj看后端接口需要进行修改
                let obj = {
                    filename: file.name,
                    taskId: fileMd5,
                    type: type,//文件类型
                    chunks: chunks,
                    force: false
                }
                this.uploadFile(ckunkFile, obj, result.data.missChunkList, 3, prompt, promptType);
                this.$refs.input_file.value = null;
            },
    
  6. 上传文件

     // 上传文件 
            uploadFile(ckunkFile, obj, missChunkList, poolLimit = 3) {
                return this.asyncPool(poolLimit, ckunkFile, (item, index) => {
                    if (Array.isArray(missChunkList) && !missChunkList?.includes(index)) {
                        let res = Promise.resolve()
                        return res
                    } else {
                        let formData = new FormData();
                        formData.set("filename", obj.filename); // 文件名
                        formData.set("taskId", obj.taskId); // 上传任务ID(UUID)同一文件的不同分块的taskId相同
                        formData.set("type", obj.type); // 上传任务ID(UUID)同一文件的不同分块的taskId相同
                        formData.set("chunks", obj.chunks); // 总块数
                        formData.set("force", false); // 存放子目录名称
                        formData.set('file', item)
                        formData.set('chunk', index)
                        this.loading = this.$loading({ text: prompt + '...' });
                        let res = this.$mApi.attachmentChunk(formData, index).then(async (result) => {
                            let { data, code, msg, usrMsg } = result
                            if (code == 4) {
                                Message.error(usrMsg)
                                this.loading.close();
                                return;
                            }
                            if (data.progress > 0 && data.progress < 1) {
                                this.loading.setText(prompt + ':' + (data.progress * 100).toFixed(2) + '%')
                            }
                            if (data.progress == 1) {
                                //处理上传完成的逻辑
                            }
                        })
                        return res
    
                    }
    
                });
            },
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值