前端上传大文件:分片、断点续传与秒传的落地

Cold 发布于 2019-06-18 26 次阅读 403 字 预计阅读时间: 2 分钟 前端开发


需求是上传几百 MB 到几个 GB 的视频文件,直接 FormData 丢上去肯定不行:超时、断网重来、服务器内存爆。分片是必须的。

整体流程

  1. 前端计算文件 hash(可以用 spark-md5 增量计算)
  2. 把 hash 发给后端,问「这个文件传过没」——传过直接秒传
  3. 没传过,问「哪些分片已经传了」——返回已传分片索引
  4. 前端只传缺失的分片
  5. 全传完通知后端合并

关键点:hash 计算别阻塞 UI

大文件算 MD5 很慢,放主线程会卡死。用 Web Worker,或者用 requestIdleCallback 分片算。spark-md5 支持增量计算,配合 FileReader 的 slice 读。

const chunkSize = 2 * 1024 * 1024; // 2MB
const chunks = Math.ceil(file.size / chunkSize);
const spark = new SparkMD5.ArrayBuffer();
const reader = new FileReader();

function loadNext(i) {
  if (i >= chunks) {
    const hash = spark.end();
    checkFile(hash);
    return;
  }
  const blob = file.slice(i * chunkSize, (i + 1) * chunkSize);
  reader.readAsArrayBuffer(blob);
}
reader.onload = e => {
  spark.append(e.target.result);
  loadNext(currentIndex++);
};

并发控制

别一次性发几百个请求,浏览器会崩。控制在 3~6 个并发,用 Promise 池或者 p-limit。

断点续传

刷新页面后,重新算 hash(或者把 hash 存 localStorage),再问一次后端已传分片,接着传。后端每个分片单独存临时文件,合并时按顺序拼。

踩过的坑

  • 分片太小请求太多,太大单请求超时,2~5MB 比较合适
  • 合并时后端要校验总分片数和文件大小
  • 临时分片要设过期清理,不然磁盘会被撑爆
  • 秒传要防伪造,hash 不能完全信前端
和喜欢的一切在一起~
最后更新于 2019-06-18