前端大文件分片上传

北凉柿子i 2024-08-19 15:33:04 阅读 71

1.分片上传整体流程

开始上传:前端启动文件分片上传。后端返回唯一标识。分片上传:获取到上传的文件,然后设置一个固定的分片大小,将文件切成多个小片,计算出每一个分片的MD5值(32位)。将每个分片的内容和MD5标识符一同上传至服务器。服务端接收每个分片及相关信息后,通过对每个分片进行校验,来确保分片的完整性。结束上传:当分片上传完毕或者前端取消上传时,调用结束上传接口结束此次文件上传操作。结束上传时,服务端判断是正常结束或取消上传来决定后续操作。

2.前端具体流程

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

3.部分代码

<code>//文件切片,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 [chunks, totalChunks, fileSize, fileName, chunkSize] // 返回存储所有文件的分片信息的数组

}

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 [chunks, totalChunks, fileSize, fileName, chunkSize] = 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[i];

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

}

}

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。