0


【VUE】前端阿里云OSS断点续传,分片上传

什么是OSS:

数据以对象(Object)的形式存储在OSS的存储空间(Bucket )中。如果要使用OSS存储数据,您需要先创建Bucket,并指定Bucket的地域、访问权限、存储类型等属性。创建Bucket后,您可以将数据以Object的形式上传到Bucket,并指定Object的文件名(Key)作为其唯一标识。

分片上传:

在上传大文件(超过5 GB)到OSS的过程中,如果出现网络中断、程序异常退出等问题导致文件上传失败,您需要使用分片上传的方式上传大文件。分片上传通过将待上传的大文件分成多个较小的碎片(Part),充分利用网络带宽和服务器资源并发上传多个Part,加快上传完成时间,并在Part上传完成之后调用CompleteMultipartUpload接口将这些Part组合成一个完整的Object。

断点续传上传:

在上传大文件(超过5 GB)到OSS的过程中,如果出现网络中断、程序异常退出等问题导致文件上传失败,甚至重试多次仍无法完成上传,您需要使用断点续传上传的方式。断点续传上传将需要上传的大文件分成多个较小的分片并发上传,加速上传完成时间。如果上传过程中,某一分片上传失败,再次上传时会从Checkpoint文件记录的断点继续上传,无需重新上传所有分片。上传完成后,所有分片将合并成完整的文件。

一.验证服务端是否已有该文件、该文件是否已上传部分块?

点击上传文件→选择文件后→读取文件信息→计算MD5值→发送给服务器→查看服务器返回信息

计算MD5:

import SparkMD5 from 'spark-md5';
            this.calculateMD5(files.raw, this.sliceSize)
                .then(async (md5) => {
                    console.log(md5)
                })
                .catch((error) => {
                    this.$message.error(error);
                });

        // md5 计算
        calculateMD5(file, chunkSize) {
            return new Promise((resolve, reject) => {
                const chunks = Math.ceil(file.size / chunkSize);
                let currentChunk = 0;
                const spark = new SparkMD5.ArrayBuffer();
                const fileReader = new FileReader();
                fileReader.onload = (e) => {
                    spark.append(e.target.result); // 更新 MD5 值
                    currentChunk++;
                    if (currentChunk < chunks) {
                        this.loadNextChunk(fileReader, currentChunk, chunkSize, file);
                    } else {
                        const md5 = spark.end(); // 计算最终 MD5 值
                        resolve(md5);
                    }
                };
                fileReader.onerror = function (e) {
                    reject(e);
                };
                this.loadNextChunk(fileReader, currentChunk, chunkSize, file);
            });
        },
        // md5 计算
        loadNextChunk(fileReader, currentChunk, chunkSize, file) {
            const start = currentChunk * chunkSize;
            const end = Math.min(start + chunkSize, file.size);
            const chunk = file.slice(start, end);
            fileReader.readAsArrayBuffer(chunk);
        },

二 文件切块

            sliceSize: 1024 * 1024 * 2, // 分片上传-每片5MB
            fileBlockArr: [],

                        _this.fileBlockArr = [];
                        let blob = file.raw;
                        const { size: fileSize, name: fileName } = blob;
                        //计算文件切片总数,Math.ceil向上取整
                        const totalSlice = Math.ceil(fileSize / _this.sliceSize);
                        _this.chunktotle = totalSlice;
                        for (let i = 0; i < totalSlice; i++) {
                            let start = i * _this.sliceSize;
                            let end = Math.min(fileSize, start + _this.sliceSize);
                            let chunkBlob = blob.slice(start, end); // 直接使用slice方法分片Blob对象
                            _this.fileBlockArr.push(chunkBlob);
                        }

三 调用初始化接口

调用初始化接口,拿到部分参数后进行块上传

                        const requestList = [];
                        _this.fileBlockArr.forEach((item, index) => {
                            if (index + 1 >= sliceNo) {
                                const fn = () => {
                                    let data = new FormData();
                                    data.append('file', item);
                                    data.append('fileSlicesNum', totalSlice);
                                    data.append('sliceNo', index + 1);
                                    data.append('fileMD5', md5);
                                    data.append('resourceKey', _this.resourceKey);
                                    data.append('ossSlicesId', _this.ossSlicesId);
                                    data.append('fileSize', fileSize);
                                    return axios({
                                        url: process.env.VUE_APP_BASE_API + '/common/partUpload',
                                        method: 'post',
                                        data: data,
                                        headers: {
                                            Authorization: 'Bearer ' + getToken(),
                                            'Content-Type': 'multipart/form-data',
                                        },
                                    })
                                        .then((res) => {
                                            if (res.data.code === 200) {
                                                if (_this.percentCount === 0) {
                                                    // 避免上传成功后会删除切片改变 chunkList 的长度影响到 percentCount 的值
                                                    _this.percentCount = 100 / _this.fileBlockArr.length;
                                                }
                                                // _this.percent += _this.percentCount; // 改变进度
                                                _this.percent = res.data.progressPercent;
                                                _this.fileBlockArr.splice(index, 1); // 一旦上传成功就删除这一个 chunk,方便断点续传
                                            }
                                        })
                                        .catch((err) => {
                                            return Promise.reject(err);
                                        });
                                };
                                requestList.push(fn);
                            }
                        });
                        let i = 0; // 记录发送的请求个数

四 上传与合并

                        let i = 0; // 记录发送的请求个数

                        const complete = () => {
                            let completedData = {
                                ossSlicesId: _this.ossSlicesId,
                                resourceKey: _this.resourceKey,
                                fileMD5: _this.md5,
                            };
                            completePart(completedData)
                                .then((response) => {
                                    if (response.code == 200) {
                                        _this.form.versionUrl = response.data;
                                    }
                                })
                                .catch((error) => {
                                    console.log('completePart-error:', error);
                                });
                        };
                        const send = async () => {
                            if (i >= requestList.length) {
                                complete();
                                return;
                            }
                            await requestList[i]();
                            i++;
                            send();
                        };
                        send(); // 发送请求

全览

data(){
    return {
            sliceSize: 1024 * 1024 * 2, // 分片上传-每片5MB
            chunktotle: 0,
            percentCount: 0,
            percent: 0,
            md5: '',
            fileBlockArr: [],
            resourceKey: undefined,
            ossSlicesId: undefined,
            fileName: undefined,
            }
    },

methods:{
        uploadChange(files, fileList) {
            this.fileName = files.name;
            this.calculateMD5(files.raw, this.sliceSize)
                .then(async (md5) => {
                    this.md5 = md5;
                    // 分片验证
                    let checkData = {
                        fileMD5: this.md5,
                    };
                    continueUpload(checkData)
                        .then((response) => {
                            if (response.code === 200) {
                                if (
                                    response.data !== null &&
                                    response.data.sliceNo < response.data.fileSlicesNum
                                ) {
                                    //已存在,但未上传完成 取已上传到的块信息
                                    this.resourceKey = response.data.resourceKey;
                                    this.ossSlicesId = response.data.ossSlicesId;
                                    let sliceNo = response.data.sliceNo;
                                    blockUpload(files, sliceNo + 1);
                                } else if (
                                    response.data !== null &&
                                    response.data.sliceNo == response.data.fileSlicesNum
                                ) {
                                    // 上传完成待合并
                                    let completedData = {
                                        ossSlicesId: response.data.ossSlicesId,
                                        resourceKey: response.data.resourceKey,
                                        fileMD5: _this.md5,
                                    };
                                    mergePart(completedData);
                                } else {
                                    // 未上传
                                    partInit({
                                        fileName: files.name,
                                        storageDirectory: 'internalApp',
                                    }).then((response) => {
                                        if (response.code === 200) {
                                            this.resourceKey = response.data.resourceKey;
                                            this.ossSlicesId = response.data.ossSlicesId;
                                            blockUpload(files, 0);
                                        }
                                    });
                                }
                            }
                        })
                        .catch((error) => {
                            console.log('continueUpload-error:', error);
                        });

                    function mergePart(completedData) {
                        completePart(completedData)
                            .then((response) => {
                                if (response.code == 200) {
                                    let fileData = {
                                        name: _this.fileName,
                                        url: response.data,
                                    };
                                    _this.fileList.push(fileData);
                                    _this.form.versionUrl = response.data;
                                }
                            })
                            .catch((error) => {
                                console.log('completePart-error:', error);
                            });
                    }

                    let _this = this;
                    function blockUpload(file, sliceNo) {
                        _this.fileBlockArr = [];
                        let blob = file.raw;
                        const { size: fileSize, name: fileName } = blob;
                        //计算文件切片总数,Math.ceil向上取整
                        const totalSlice = Math.ceil(fileSize / _this.sliceSize);
                        _this.chunktotle = totalSlice;
                        for (let i = 0; i < totalSlice; i++) {
                            let start = i * _this.sliceSize;
                            let end = Math.min(fileSize, start + _this.sliceSize);
                            let chunkBlob = blob.slice(start, end); // 直接使用slice方法分片Blob对象
                            _this.fileBlockArr.push(chunkBlob);
                        }
                        const requestList = [];
                        _this.fileBlockArr.forEach((item, index) => {
                            if (index + 1 >= sliceNo) {
                                const fn = () => {
                                    let data = new FormData();
                                    data.append('file', item);
                                    data.append('fileSlicesNum', totalSlice);
                                    data.append('sliceNo', index + 1);
                                    data.append('fileMD5', md5);
                                    data.append('resourceKey', _this.resourceKey);
                                    data.append('ossSlicesId', _this.ossSlicesId);
                                    data.append('fileSize', fileSize);
                                    return axios({
                                        url: process.env.VUE_APP_BASE_API + '/common/partUpload',
                                        method: 'post',
                                        data: data,
                                        headers: {
                                            Authorization: 'Bearer ' + getToken(),
                                            'Content-Type': 'multipart/form-data',
                                        },
                                    })
                                        .then((res) => {
                                            if (res.data.code === 200) {
                                                if (_this.percentCount === 0) {
                                                    // 避免上传成功后会删除切片改变 chunkList 的长度影响到 percentCount 的值
                                                    _this.percentCount = 100 / _this.fileBlockArr.length;
                                                }
                                                // _this.percent += _this.percentCount; // 改变进度
                                                _this.percent = res.data.progressPercent;
                                                _this.fileBlockArr.splice(index, 1); // 一旦上传成功就删除这一个 chunk,方便断点续传
                                            }
                                        })
                                        .catch((err) => {
                                            return Promise.reject(err);
                                        });
                                };
                                requestList.push(fn);
                            }
                        });
                        let i = 0; // 记录发送的请求个数

                        const complete = () => {
                            let completedData = {
                                ossSlicesId: _this.ossSlicesId,
                                resourceKey: _this.resourceKey,
                                fileMD5: _this.md5,
                            };
                            mergePart(completedData);
                        };
                        const send = async () => {
                            if (i >= requestList.length) {
                                complete();
                                return;
                            }
                            await requestList[i]();
                            i++;
                            send();
                        };
                        send(); // 发送请求
                    }
                })
                .catch((error) => {
                    this.$message.error(error);
                });
        },
        // md5 计算
        calculateMD5(file, chunkSize) {
            return new Promise((resolve, reject) => {
                const chunks = Math.ceil(file.size / chunkSize);
                let currentChunk = 0;
                const spark = new SparkMD5.ArrayBuffer();
                const fileReader = new FileReader();
                fileReader.onload = (e) => {
                    spark.append(e.target.result); // 更新 MD5 值
                    currentChunk++;
                    if (currentChunk < chunks) {
                        this.loadNextChunk(fileReader, currentChunk, chunkSize, file);
                    } else {
                        const md5 = spark.end(); // 计算最终 MD5 值
                        resolve(md5);
                    }
                };
                fileReader.onerror = function (e) {
                    reject(e);
                };
                this.loadNextChunk(fileReader, currentChunk, chunkSize, file);
            });
        },
        // md5 计算
        loadNextChunk(fileReader, currentChunk, chunkSize, file) {
            const start = currentChunk * chunkSize;
            const end = Math.min(start + chunkSize, file.size);
            const chunk = file.slice(start, end);
            fileReader.readAsArrayBuffer(chunk);
        },
}
标签: 前端

本文转载自: https://blog.csdn.net/liusuihong919520/article/details/136833741
版权归原作者 SZnA1 所有, 如有侵权,请联系我们删除。

“【VUE】前端阿里云OSS断点续传,分片上传”的评论:

还没有评论