大文件分片上传、断点续传

一、文件上传


<el-upload
    ref="localUpload"
    class="avatar-uploader"
    action="#"
    multiple
    :accept="acceptType"
    :on-change="handleChange"
    :show-file-list="false"
    :auto-upload="false">
    <i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<el-button v-if="!isWatch" type="primary" @click="confirmAdd">保存</el-button>

二、获取文件

handleChange(file) {
    // 获取文件的路径(此处的路径为安全路径)
    let path = document.getElementsByClassName('el-upload__input')[0].value;
    path = path.substring(0, path.lastIndexOf('\\') + 1);
    const oid = this.newId = uuidV4().replace(/-/g, '');
    // 获取文件类型
    this.acceptType = file.raw.type;
    // 创建文件预览路径
    const url = URL.createObjectURL(file.raw);
    // 判断文件是否上传过,根据图片的路径以及图片的大小判断
    const historyObj = _.find(this.localStorageUrlList, item => item.path === (path + file.name) && file.size === item.size);
    if (historyObj) {
        this.$set(historyObj, 'url', url);
        this.urlList.push(historyObj);
        // 获取分片未全部上床的记录
        if (historyObj.status === 'exception') {
            // 从未上传成功分片数继续分片上传
            this.uploadByPieces(file.raw, Math.ceil(file.raw.size / 1024 / 1024), historyObj.oid, historyObj.successNumber);
        }
    } else {
        this.urlList.push({
            url, // 文件预览路径
            size: file.size, // 文件大小
            path: path + file.name, // 文件本地路径
            oid, // 文件唯一标识,用户分片合并
            status: 'success'
        });
        // 文件分片数向上取整
        this.uploadByPieces(file.raw, Math.ceil(file.raw.size / 1024 / 1024), oid);
    }
}

三、文件分片

uploadByPieces(file, pieces, oid, start = 0) {
    const list = [];
    const urlObj = _.find(this.urlList, item => item.oid === oid);
    for (let i = start; i < pieces; i++) {
        const chunkSize = 1024 * 1024; // 文件分片大小
        const start = i * chunkSize; // 文件分片开始位置
        const end = Math.min(file.size, start + chunkSize); // 文件分片结束位置
        const blob = file.slice(start, end); // 文件分片内容
        const formData = new FormData();
        formData.append('identifier', oid); // 文件唯一标识
        formData.append('file', blob); // 文件二进制内容
        formData.append('number', i);
        list.push({ formData, status: '未上传' });
        if (i === pieces - 1) {
            urlObj.list = list;
            urlObj.pieces = pieces;
        }
    }
}

四、文件多任务上传

confirmAdd() {
    const localStorageObj = {};
    localStorageObj['form'] = this.dataSourceForm;
    localStorageObj['acceptType'] = this.acceptType;
    Promise.all(this.urlList.map(item => {
        return this.limitedRequest(localStorageObj, item);
    })).then(resp => {
        const list = resp.filter(item => item.status === 'exception');
        localStorageObj['urlList'] = resp;
        if (list.length > 0) {
            setUploadList(JSON.stringify(localStorageObj));
        } else {
            setUploadList(JSON.stringify(localStorageObj));
            this.mergeFile(localStorageObj);
        }
    });
}
limitedRequest(localStorageObj, obj, maxNum = 10) {
    const _this = this;
    const list = obj.list;
    const pool = [];
    const initSize = Math.min(list.length, maxNum);
    let index = obj.successNumber || 0;
    for (let i = 0; i < initSize; i++) {
        pool.push(run(list.splice(0, 1)));
    }
    function r() {
        if (list.length === 0) {
            return Promise.resolve();
        }
        return run(list.splice(0, 1));
    }
    function run(item) {
        return new Promise((resolve, reject) => {
            uploadSliceFile(item[0].formData, _this.abortController).then(() => {
                _this.$set(obj, 'successNumber', index);
                _this.$set(obj, 'status', 'success');
                _this.$set(obj, 'percent', (((index + 1) / obj.pieces) * 100).toFixed(2));
                index++;
                resolve(r());
            }).catch(err => {
                console.log(err);
                // 上传错误停止所有已发起的请求,以防分片上传错误重复,须在需要停止的接口中添加参数signal: abortController.signal
                _this.abortController.abort();
                _this.$set(obj, 'successNumber', index);
                _this.$set(obj, 'status', 'exception');
                reject(err);
            });
        });
    }
    return Promise.allSettled(pool).then(() => {
        return obj;
    });
}

五、文件分片合并

mergeFile(localStorageObj) {
    const { task_id, name, oid } = this.dataSourceForm;
    const type = this.acceptType.split('/')[1];
    Promise.all(this.urlList.map(item => {
        if (!item.isMerged) {
            return mergeFile(item.oid, type).then(resp => {
                item.isMerged = true;
                item.name = resp.file_name;
                return resp.file_name;
            });
        } else {
            return item.name;
        }
    })).then(resp => {
        this.$message({
	        message: '合并成功',
	        type: 'success'
	    });
    }).catch(() => {
    	// 上传进度保存在本地
        setUploadList(JSON.stringify(localStorageObj));
    });
}
  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值