跳到主要内容

如何实现大文件切片上传

· 阅读需 3 分钟
素明诚
Full stack development

为什么要大文件分割上传?

  1. 文件过大会因为网络环境不文件导致超时或者失败
  2. 分割成小块后可以提高上传的效率,更有效的利用带宽,提高上传速度
  3. 如果上传一半突然失败,可以向服务端获取一下进度,重新上传

前端分割文件

  1. 使用input获取文件,生成文件的MD5 ,在上传文件的时候,可以先生成文件的 MD5,把 hash 发给后端方便后端校验。

  2. 使用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) {
// 上传
}

  1. 向服务端发送上传文件的MD5值和分片数量,开始上传

  2. 初始化每一个分片上传任务,返回本次分片上传唯一标识

  3. 上传失败要返回:分片信息、文件名称、文件hash、分片大小、分片序号等,可以使用Promise.allSettled()

  4. 上传成功:服务端合并文件,校验文件hash,得到原始文件,返回成功信息

断点续传

断点续传其实就是让请求可中断,然后在接着上次中断的位置继续发送,此时要保存每个请求的实例对象,以便后期取消对应请求,并将取消的请求保存或者记录原始分块列表取消位置信息等,以便后期重新发起请求

  1. 如果使用原生 XHR 可使用 (new XMLHttpRequest()).abort() 取消请求
  2. 如果使用 axios 可使用 new CancelToken(function (cancel) {}) 取消请求
  3. 如果使用 fetch 可使用 (new AbortController()).abort() 取消请求

上传过程中刷新页面怎么办

你可以每次上传成功后将成功的分片信息保存在本地,刷新后再读取一下,再继续传

如何进行并行上传

  1. 使用Promise.race()来同时请求,用Promise.all()来确定都完成了
  2. 巨大的话会导致线程频繁切换,所以并行上传要考虑数量

大文件 MD5 时的优化

在使用js-md5生成 MD5 的时候,页面可能会假死,可以使用worker线程进行大文件md5加密的优化,防止页面卡死

如何实现秒传

秒传就是服务器有这个文件,所以你可以在上传前先请求一下服务器,看看hash是否一致,如果有直接返回上传成功。