火影 发表于 2024-6-11 11:04:30

前端大文件分片上传

1.分片上传整体流程



[*]开始上传:前端启动文件分片上传。后端返回唯一标识。
[*]分片上传:获取到上传的文件,然后设置一个固定的分片巨细,将文件切成多个小片,盘算出每一个分片的MD5值(32位)。将每个分片的内容和MD5标识符一同上传至服务器。服务端接收每个分片及相干信息后,通过对每个分片进行校验,来确保分片的完整性。
[*]竣事上传:当分片上传完毕或者前端取消上传时,调用竣事上传接口竣事此次文件上传操纵。竣事上传时,服务端判定是正常竣事或取消上传来决定后续操纵。
https://img-blog.csdnimg.cn/6a96c6a26d6f4e71a237eee4ba1d4cba.png
2.前端具体流程



[*]开始上传,发送开始上传请求,向服务器转达文件名、文件总巨细、分片总数和切片巨细,获取并保存文件上传的唯一标识符。同时在发送请求前,对上传的文件名进行校验,如果文件名超过最大长度256,则克制发送请求并向用户提示修改文件名称。
[*]分片上传,起首将文件进行切片,调用切片方法时,必要将文件转达给该方法,然后根据文件的巨细来决定每个分片的巨细并切分成多个片段,同时盘算出总切片数,并为每个切片添加从0开始的顺序索引。随后对每个切片盘算出他们的MD5值。最后把这些分片的MD5值和顺序索引保存在浏览器内存中。然后发奉上传数据请求,向服务器发送唯一标识符、分片的顺序索引、分片数据,MD5值和当前分片的巨细。在每个分片发送请求后,如果发送乐成,则将其对应的信息从浏览器内存中删除,并盘算出此时的上传进度,然后发送下一个片段直至最后。如果全部上传乐成,清空浏览器内存。如果请求发生错误,则对该分片再次发送一次上传请求,如果仍然错误,不再上传,调用竣事请求并提示错误原因。
[*]竣事上传,如果文件分片全部乐成上传,向服务器发送竣事请求,转达正常竣事状态码,清空浏览器内存。如果自动取消上传则转达取消请求状态码,同时清空浏览器内存,不再继续上传。
3.部分代码

//文件切片,utils

import SparkMD5 from 'spark-md5'

export async function getChunkList (files) {
        const file = files
        console.log(file);
        const fileSize = file.size // 文件大小
        const fileName = file.name

        let chunkSize = 0;
        if (fileSize <= 5 * 1024 * 1024) { // 0-5M,不分片
                chunkSize = fileSize;
        } else if (fileSize <= 20 * 1024 * 1024) { // 5-20M,每个分片大小1M
                chunkSize = 1024 * 1024;
        } else if (fileSize <= 50 * 1024 * 1024) { // 20-50M,每个分片大小2M
                chunkSize = 2 * 1024 * 1024;
        } else if (fileSize <= 100 * 1024 * 1024) { // 50-100M,每个分片大小4M
                chunkSize = 4 * 1024 * 1024;
        } else if (fileSize <= 200 * 1024 * 1024) { // 100-200M,每个分片大小6M
                chunkSize = 6 * 1024 * 1024;
        } else if (fileSize <= 500 * 1024 * 1024) { // 200-500M,每个分片大小10M
                chunkSize = 10 * 1024 * 1024;
        } else if (fileSize <= 1024 * 1024 * 1024) { // 500M-1G,每个分片大小20M
                chunkSize = 20 * 1024 * 1024;
        } else { // 1G以上,每个分片大小20M
                chunkSize = 20 * 1024 * 1024;
        }
        const totalChunks = Math.ceil(fileSize / chunkSize)
        let start = 0
        let end = Math.min(chunkSize, fileSize)
        let index = 0 // 分片索引,从0开始
        const chunks = [] // 存储当前文件的分片信息的数组

        while (start < fileSize) {
                const chunk = file.slice(start, end)
                const reader = new FileReader()
                const promise = new Promise((resolve, reject) => {
                        reader.onload = (e) => { //读取文件分片信息,使用SparkMD5库计算分片的MD5值
                                const spark = new SparkMD5.ArrayBuffer()
                                spark.append(e.target.result)
                                resolve(spark.end())
                        }
                        reader.onerror = (err) => {
                                reject(err)
                        }
                })
                reader.readAsArrayBuffer(chunk)
                try {
                        const md5 = await promise
                        const chunkInfo = { chunk, md5, index }
                        chunks.push(chunkInfo)
                } catch (err) {
                        reject(err)
                }
                //更新循环起止位置
                start = end
                end = Math.min(start + chunkSize, fileSize)
                index++
        }
        // 将当前文件的分片信息数组存入总的数组中
        return // 返回存储所有文件的分片信息的数组
}         async handleUpload () {
                        this.progressState = 'upload'
                        this.wrongNum = 0
                        if (this.fileList.length == 0) { //判断是否添加文件
                                this.$notify.warning({
                                        title: this.$global.warningMessage.title,
                                        message: this.$global.warningMessage.fileMessage,
                                });
                                return
                        }
                        if (this.file.name.length > 256) {
                                this.$notify.warning({
                                        title: this.$global.warningMessage.title,
                                        message: this.$global.warningMessage.fileNameMessage,
                                });
                                return
                        }
                        this.totalSize = this.file.size
                        // 调用getChunkList方法获取分片及相关信息
                        const = await getChunkList(this.file.raw)
                        this.totalChunks = totalChunks
                        this.fileSize = fileSize
                        this.fileName = fileName
                        this.chunkList = chunks
                        this.chunkSize = chunkSize
                        console.log(this.chunkList);
                        // 开始上传请求
                        await this.startUpload()
                        // 遍历分片信息数组,取出除文件分片外的其他信息
                        const sessionChunkList = this.chunkList.map(({ chunk, ...rest }) => rest);
                        // 将分片其他信息存入sessionStorage中
                        sessionStorage.setItem("chunkData", JSON.stringify(sessionChunkList));
                        let i = 0;
                        while (i < this.chunkList.length && this.wrongNum < 1) { //对分片数组进行遍历
                                const chunkInfo = this.chunkList;
                                const res = await this.uploadChunk(chunkInfo); //调用上传分片方法
                                if (res.data.state == 200) {
                                        const removeInfo = {
                                                md5: chunkInfo.md5,
                                                index: chunkInfo.index
                                        }
                                        await this.handleSuccess(removeInfo, chunkInfo) //调用当前分片上传成功处理函数
                                        i++;
                                } else {   //上传未成功,重新上传一次
                                        const res = await this.uploadChunk(chunkInfo);
                                        this.wrongNum += 1
                                        if (res.data.state == 200) {
                                                this.wrongNum = 0
                                                await this.handleSuccess(removeInfo, chunkInfo)
                                                i++;
                                        } else {// 重新上传一次后仍未成功
                                                const state = this.$global.completeUploadState.cancelUpload
                                                await this.completeUpload(state)
                                                this.$notify.error({
                                                        title: this.$global.failedMessage.title,
                                                        message: res.data.message
                                                });
                                                this.handleClear()
                                                return
                                        }
                                }
                        }
                }

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 前端大文件分片上传