White Meta
登录Admin
语言
中文EN
Theme: ...

© 2026 White Meta

回到顶部

返回文章列表


title: 大文件上传
web_id: 49
pulled_at: 2026-06-25T15:30:39

方案:

普遍性方案是文件分片,把大文件打散成小事物,从而降低上传失败的风险。

实现涉及到诸多技术细节。

比如底层协议标准如何制定,协议标准决定了前后端如何交互,决定了前后端代码如何开发。

除了协议之外,还涉及前端如何控制并发,如何高效分片,设计后端如何存储分片,如何高效合并分片,如何保证分片唯一性等。

市面上没有统一解决方案,虽然有公有云上的oss,但我们的产品可能会部署到私有云,所以最稳妥办法还是自行实现。

实现:

客户端分片,计算分片哈希和完整哈希,再使用哈希和服务器换取当前文件信息。

计算哈希是cpu密集型操作,会导致长时间ui阻塞。虽然可以用web worker来加速,但经过我的测试,即便是使用了多线程,超大文件(10g以上)的阻塞仍然超过30s。这是不能接受的。

因此,我对上传流程做出优化,我假定大部分文件都是一个新文件,于是在流程上我允许用户在获得完整hash之前直接上传分片,这样一来几乎可以零延迟到上传,等到整体hash计算出来之后再向服务器补充hash数据。

四个通信协议:

  1. 创建文件协议,前端用head请求,换取上传唯一token,后续请求必须携带该token
  2. 哈希校验,前端把某个分片hash,或者整个文件hash发送给服务器,得到分片和文件状态。
  3. 分片上传协议,前端将分片的二进制数据发送到服务器存储。
  4. 分片合并协议,前端提示服务器可以完成分片合并。

因为涉及bff层,所以需要编写服务端代码。最大的挑战是如何保证每个分片的唯一性。这种唯一性既包含了存储的唯一性,也包含传输的唯一性。

存储的唯一性保证分片不会重复保存,避免数据的冗余。

传输的唯一性保证分片不会重复上传,避免通信的冗余。

要保证分片不会重复保存,就必须让分片和文件解偶,没有从属关系。文件独立记录,按照顺序依次指向不同分片。

要保证分片不重复上传,就必须让分片永不删除。如果合并文件之后删除分片就会导致下次重复上传找不到对应分片,必须重复上传。

最后是合并分片逻辑,如果真把分片合并成一个大文件,大文件的数据实际上是冗余的,整个过程也极其耗时。所以我做了这样的一些处理。当服务器收到合并请求时,服务器只需要做一些简单校验(文件大小,分片数量)就可以了,只需要生成url即可。当用户下载文件时,使用文件流依次读取分片数据,用流管道直接响应给客户端即可。

合并和文件访问效率都很高,服务器也不会有冗余存储。

0创建文件协议

先告知服务器要传文件了,需要服务器返回唯一标识。

1文件切片

const chunkSize = 5 * 1024 * 1024; // 5MB
const chunks = [];
let cur = 0;
while (cur < file.size) {
    chunks.push(file.slice(cur, cur + chunkSize));
    cur += chunkSize;
}

2生成指纹

为了标识文件的唯一性(用于秒传和断点续传),需要计算文件的 MD5 或 SHA-256。

3秒传校验 (Fast Upload)

在正式上传前,先发一个请求给服务端,带上文件哈希值。

  • 场景 A: 服务端已存在该文件 -> 直接返回“上传成功”(秒传)。
  • 场景 B: 服务端已存在部分分片 -> 返回已收到的分片索引列表(断点续传)。
  • 场景 C: 服务+端不存在该文件 -> 开始完整上传。

4并发上传

使用 FormData 包装每个分片,并通过 XMLHttpRequest 或 Fetch 发送。

  • 并发控制: 不要同时发起成百上千个请求。建议维护一个发送队列,将并发数控制在 3-6 个,以防占用过多浏览器资源导致页面卡顿。
  • 重试机制: 单个分片上传失败时,应支持自动重试(如重试 3 次)。

5合并请求

当所有分片上传完成后,前端发送一个“合并”指令给服务端。

服务端根据分片索引将文件块合并成原始文件,并校验合并后的哈希值是否一致。

项目亮点

  • 参与项目通用库开发

  • 亮点:从0-1开发upload-sdk,该sdk为所有文件上传,特别是大文件上传场景提供前后端的支撑,统一了所有文件上传的开发方式。完成了从底层协议到工具类/前端组件/后端中间件的开发。

    在实现层面,为保证使用的灵活性,利用多种设计模式完成了sdk和上层应用的完全解偶,并对服务器存储进行精细设计,保证了文件传输和存储的唯一性。