vue 大文件断点续传、并发控制、请求重试、多线程计算md5

1.template

<a-upload :file-list="objData.fileList" name="file" :beforeUpload="beforeUpload" class="zlcUpload">
     <a-button>
         <a-icon type="cloud-upload" />
     </a-button>
</a-upload>
<a-button size="large" type="primary" :disabled="!objData.fileList.length" @click="upload" class="zlcBtn">
   上传
</a-button>

2.script

data() {
        return {
            objData: {
                chunkSize: 1 * 1024 * 1024,
                fileList: [],
                sliceList: [],
                md5: null,
                fileName: null,
                finishSliceList: [],
                uploadStart: 0
            },
            visible: false,
            uploadCompleted: false,
        }
    },

 methods: {
        beforeUpload(file) {
            this.objData.fileList = [file];
            return false;
        },
}

3.点击上传,进行切片,以及获取文件名,md5唯一标识

async upload() {
            this.visible = true;
            let file = this.objData.fileList[0];
            this.objData.fileName = file.name;
            let fileMd5 = await this.getFileMd5(file);

            if (file.size < this.objData.chunkSize) {

            } else {
                this.objData.sliceList = this.getSliceList(file);

                this.hadUpload().then(res => {
                    if (res.message == '上传成功') {
                        this.objData.uploadStart = this.objData.sliceList.length
                        this.uploadCompleted = true
                        return
                    }
                    let finishArr = res.result;
                    this.objData.uploadStart = !finishArr ? this.objData.sliceList.length : finishArr.length;
                    if (!finishArr.length ||( finishArr.length < this.objData.sliceList.length)) {
                        //并发请求
                        this.concurRequest(this.objData.sliceList, 3).then(res => {
                            let { md5, fileName } = this.objData;
                            console.log('并发请求结果', res)
                            let rejectResp = res.some(item => item.code != 200) //是否有失败的
                            if(!rejectResp){ //全部成功
                                mergeFile({ md5, fileName }).then(res => {
                                    this.uploadCompleted = true
                                })
                            }else{
                                //至少一个失败
                            }
                           
                        })
                    }else{
                        
                    }
                    // //开始上传
                    // let uploadList = this.objData.sliceList.map(item => {

                    //     if (finishArr.includes(item.flag.toString())) {

                    //     } else {
                    //         return new Promise((resolve, reject) => {
                    //             const formData = new FormData();
                    //             formData.append(`file`, item.blob);
                    //             formData.append(`index`, item.flag);
                    //             formData.append(`md5`, fileMd5);
                    //             formData.append(`filename`, file.name);

                    //             uploadFile(formData).then(res => {
                    //                 this.objData.uploadStart++;
                    //                 resolve(res);
                    //             }).catch(err => {
                    //                 reject(err);
                    //             })
                    //         })
                    //     }

                    // })
                    // Promise.allSettled(uploadList).then(res => {
                    //     if (res.some(item => item.status == "rejected")) {
                    //         //至少一个分片失败
                    //     } else {
                    //         let { md5, fileName } = this.objData;
                    //         mergeFile({ md5, fileName }).then(res => {
                    //             this.uploadCompleted = true
                    //         })
                    //     }
                    // })

                    
                })


            }
        },
        //多线程获取md5唯一标识
        getFileMd5(file) {
            let that = this;
            return new Promise(resolve => {
const worker = new Worker(new URL('@src/webWork/fileMd5.js', import.meta.url))
                // 向子线程发消息
                worker.postMessage(file)
                // 接收子线程发来的消息
                worker.onmessage = e => {
                    that.objData.md5 = e.data
                    resolve(that.objData.md5);
                    worker.terminate();
                }
                
            })
        },
        //开始切片
        getSliceList(file) {
            let result = [];
            let index = 0;
            for (let nowSize = 0; nowSize < file.size; nowSize += this.objData.chunkSize) {
                result.push({
                    flag: index,
                    blob: file.slice(nowSize, nowSize + this.objData.chunkSize),
                })
                index++;
            }
            return result;
        },
        //判断之前是否上传过
        hadUpload() {
            let { fileName, md5 } = this.objData;
            return new Promise((resolve, reject) => {
                hadUploadFile({ fileName, md5 }).then(res => {
                    resolve(res);
                }).catch(err => {
                    reject();
                })
            })
        },
        //并发控制
        concurRequest(urls, maxNum){
            let that = this
            return new Promise(resolve => {
                let index = 0; //指向下一次请求的Url对应的下标
                let result = [] //保存请求的结果
                let count = 0; //当前完成请求的数量
                async function _request(){
                    let i = index
                    const url = urls[index]
                    index++;
                    try{
                        // const resp = await fetch(url)
                        const formData = new FormData();
                        formData.append(`file`, url.blob);
                        formData.append(`index`, url.flag);
                        formData.append(`md5`, that.objData.md5);
                        formData.append(`filename`, that.objData.fileName);
                        const resp = await uploadFileCopy(formData, 2)
                        console.log('请求结果', resp)
                        result[i] = resp
                    }catch(err){
                        result[i] = err
                    }finally{
                        that.objData.uploadStart++;
                        count++;
                        if(count == urls.length){
                            resolve(result)
                        }
                        if(index < urls.length){
                            _request()
                        }

                    }
                }
                for(let i = 0; i < Math.min(urls.length, maxNum); i++){
                    _request()
                }
            })
        }

@src/webWork/fileMd5.js文件内容

// worker.js ---子线程
import SparkMD5 from "spark-md5"
// 接收主线程发来的消息
onmessage = (file) => {
  // 向主线程发送消息
  const reader = new FileReader();
  reader.readAsArrayBuffer(file.data);
  reader.onloadend = function (e) {
    const spark = new SparkMD5.ArrayBuffer();
    spark.append(e.target.result);
    let md5 = spark.end();
    postMessage(md5);
  };
};

4.请求重试api

//请求重试
export const uploadFileCopy = (params, maxTimes) => {
    let initTimes = 0;
    let request = () => {
        return postRequest("/file/upload", params, {"Content-Type": "multipart/form-data"}).catch(err => {
            if(initTimes < maxTimes){
                initTimes++
                return request()
            }else{
                throw new Error('请求次数已达上限!')
            }
        })
    }
    return request()
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值