如何实现大文件切片上传
· 阅读需 3 分钟
为什么要大文件分割上传?
- 文件过大会因为网络环境不文件导致超时或者失败
- 分割成小块后可以提高上传的效率,更有效的利用带宽,提高上传速度
- 如果上传一半突然失败,可以向服务端获取一下进度,重新上传
前端分割文件
-
使用
input
获取文件,生成文件的MD5
,在上传文件的时候,可以先生成文件的 MD5,把 hash 发给后端方便后端校验。 -
使用
Blob.prototype.slice
或者File.prototype.slice
方法切割文件
async function splitFile(file, chunkSize = 1024 * 1024) {
const fileSize = file.size;
const chunks = Math.ceil(fileSize / chunkSize);
let currentChunk = 0;
while (currentChunk < chunks) {
const start = currentChunk * chunkSize;
const end = start + chunkSize >= fileSize
? fileSize
: start + chunkSize;
const chunk = file.slice(start, end);
const blob = new Blob([chunk], { type: file.type });
// 对每一块进行操作
await uploadChunk(blob, currentChunk, chunks);
currentChunk++;
}
}
async function uploadChunk(chunk, index, total) {
// 上传
}
-
向服务端发送上传文件的
MD5
值和分片数量,开始上传 -
初始化每一个分片上传任务,返回本次分片上传唯一标识
-
上传失败要返回:分片信息、文件名称、文件
hash
、分片大小、分片序号等,可以使用Promise.allSettled()
-
上传成功:服务端合并文件,校验文件
hash
,得到原始文件,返回成功信息
断点续传
断点续传其实就是让请求可中断,然后在接着上次中断的位置继续发送,此时要保存每个请求的实例对象,以便后期取消对应请求,并将取消的请求保存或者记录原始分块列表取消位置信息等,以便后期重新发起请求
- 如果使用原生 XHR 可使用
(new XMLHttpRequest()).abort()
取消请求 - 如果使用 axios 可使用
new CancelToken(function (cancel) {})
取消请求 - 如果使用 fetch 可使用
(new AbortController()).abort()
取消请求
上传过程中刷新页面怎么办
你可以每次上传成功后将成功的分片信息保存在本地,刷新后再读取一下,再继续传
如何进行并行上传
- 使用
Promise.race()
来同时请求,用Promise.all()
来确定都完成了 - 巨大的话会导致线程频繁切换,所以并行上传要考虑数量
大文件 MD5 时的优化
在使用js-md5
生成 MD5 的时候,页面可能会假死,可以使用worker
线程进行大文件md5
加密的优化,防止页面卡死
如何实现秒传
秒传就是服务器有这个文件,所以你可以在上传前先请求一下服务器,看看hash
是否一致,如果有直接返回上传成功。