之前做项目的时候遇到一个文件上传的问题,当时用没有考虑文件较大时,后端响应过长导致链接断开的问题。这个需求后来取消了,不过问题一直在,最近研究了下怎么解决这个问题。代码见
https://github.com/Mng12345/big-file-upload
,核心原理是前端使用Blob.slice方法对文件进行分块,下面是分块代码:
makeChunks(file: File): FileChunks {
const fileChunks: FileChunks = {
file,
chunks: [],
};
if (file.size < this.chunkSize) {
fileChunks.chunks.push({ start: 0, end: file.size });
return fileChunks;
}
let chunksLength;
if (file.size % this.chunkSize === 0) {
chunksLength = Math.round(Math.floor(file.size / this.chunkSize));
} else {
chunksLength = Math.round(Math.floor(file.size / this.chunkSize)) + 1;
}
let leftSize = file.size;
while (leftSize > 0) {
const start = fileChunks.chunks.length * this.chunkSize;
const end =
start + this.chunkSize >= file.size
? file.size
: start + this.chunkSize;
leftSize -= end - start;
fileChunks.chunks.push({ start, end });
}
return fileChunks;
}
对每个chunk在上传时,共用一个随机生成的id用于后端合并子文件块
// 生成uid,传递给后端,后端根据uid对子文件块进行合并
const fileUid = randomId();
fileChunks.chunks.forEach(
(chunk: { start: number; end: number }, index: number) => {
const formData = new FormData();
formData.append("index", index + "");
formData.append("chunk", fileChunks.file.slice(chunk.start, chunk.end));
formData.append("name", fileChunks.file.name);
formData.append("chunksLength", fileChunks.chunks.length + "");
formData.append("uid", fileUid);
Axios.post<ChunkUploadResult>("/api/fileupload", formData)
.catch((reason) => console.error(`error: ${JSON.stringify(reason)}`))
.then((res) => {
if (typeof res === "object") {
const data = res.data;
if (isChunkUploadResult(data)) {
if (data.status) {
allChunksUploadStatus[index] = true;
// 更新上传百分比
this.uploadProcess = calUploadProcess();
// 更新上传状态
if (this.uploadProcess === 100) this.allChunksUploaded = true;
}
}
}
});
}
);