分片上传

分片上传

分片大小

不能太小:分片数量暴增,元数据压力大、请求次数多;
不能太大:网络中断后重复传输成本高;

使用固定大小5MB每个分片,最后一片允许小于分片大小。

分片唯一标识

客户端请求服务端,服务端创建上传任务,返回uploadId;

断点续传

1.服务端保存了所有分片及分片的hash值

2.客户端上传分片,服务端查询分片表,如果有hash值一样的,引用+1,返回上传成功(秒传)

hash

使用md5算法

md5算法是CPU密集型

使用多线程进行计算

每个分片有一个hash值,再根据所有分片的hash值计算出完整文件的hash值(不做完整文件的md5计算减少耗时)

服务端校验每个分片的hash值 及 完整文件的hash值。

并发控制

1.采集服务器指标用于告知客户端窗口内最大上传分片数

指标有:服务器空闲工作线程数量、磁盘IO、当前该任务堆积的分片数量

2.客户端窗口内只上传服务器下发分片数

3.服务器保存堆积的分片数量,如果下发分片数>堆积数 不允许上传,如果下发分片数 < 堆积数 允许上传。

文件合并

不做真正文件合并,只验证完整文件的hash值及是否完成上传。

过期清理

上传任务全局有效期,24h还没有变为完成的删除相关的分片。

客户端调用取消接口,分布式锁,异步发起分片删除。

定时任务 每小时进行分片清理。

分片下载

客户端请求下载返回分片列表。

断点下载

依靠 HTTP Range 断点范围请求!
服务端必须支持 Accept-Ranges: bytes;
客户端记录已下载字节偏移,续传时携带 Range: bytes=start-end;

下载并发控制

限制客户端最大并行分片数量。

同上传并发控制。

标准 Multipart上传

五步标准流程

初始化 CreateMultipartUpload

服务端生成全局唯一 uploadId(本次分片任务标识)

上传分片 UploadPart

客户端并发上传多个分片,每个分片携带:uploadId + partNumber
重点:分片顺序无关,partNumber 决定最终文件顺序,可以乱序上传

列出已上传分片 ListParts

断点续传场景调用,查询哪些分片已经上传成功

最大分片10000。

完成合并 CompleteMultipartUpload

提交一份 partNumber+ETag 清单
存储服务内部:拼接所有分片 → 生成最终完整对象
合并成功后:
桶内出现正式文件 /a/b/file.mp4
原始分片变成垃圾,OSS/MinIO 不会自动删除!

放弃上传 AbortMultipartUpload

主动终止任务,存储直接清理所有临时分片

两个终态:Complete(生成文件) / Abort(删除分片)
如果两个接口都不调用 → 分片永久残留,占用存储空间

能不能不执行 Complete?

基于原生 S3 Multipart 体系,绝对不能。
不 Complete → 不会生成正式对象,无法 GET 下载
所有分片只是临时碎片,没有独立访问地址
生命周期到期会被自动清理,数据丢失

接入模式

后端中转接收分片

流程:

前端 → 业务服务 → MinIO/OSS UploadPart

优点:

  • 服务端可以实现:任务滑动窗口 rwnd、分片并发控制、同分片分布式锁、MD5 校验(就是前面整套并发控制方案)

  • 所有请求经过应用网关,鉴权、限流、监控全部统一处理

缺点:业务服务承担文件流量,带宽压力大

前端直传(预签名 URL,线上最常用)

流程:

  1. 前端调用业务后端:initUpload,获取 uploadId
  2. 后端生成 UploadPart 预签名 URL 返回前端
  3. 浏览器直接把分片 POST 上传到 OSS/MinIO,不经过业务服务
  4. 分片全部传完,前端请求后端执行 Complete

巨大限制(重点)

分片请求直连存储,绕过你的应用服务!

  1. 你无法在服务端实现【任务级滑动窗口 in_flight 限流】

    所有分片请求不经过网关,没法拦截超限并发

  2. 无法统一拦截重复分片、实时校验分片 MD5

  3. 只能依靠前端滑动窗口做并发控制,客户端 bug 会直接打满存储

自定义分片不合并方案的下载

普通下载

网关流式拼接下载

请求链路:

浏览器 → 自研文件网关 → 查询 DB 分片索引 → 循环并行 / 串行读取各个分片 → 按顺序流式输出 Response

特点:

  • 用户侧 URL:GET /download/{fileId},和普通文件无差别;不用改前端
  • 网关不落地完整文件,内存流式转发,不占用磁盘

网关返回分片清单,客户端本地拼接

网关返回 JSON:分片地址列表、分片大小。
客户端并行下载所有分片,本地组装成完整文件。

视频在线播放

网页 <video src="xxx">、HLS/MPD 点播,底层依赖 HTTP Range 分片请求、流式输出、支持 seek 拖拽。
原生 video 标签只能访问单个资源地址,无法直接读取一堆分片。
提供两套落地路线:

路线 A:网关代理 + 支持 Range 请求【Web 首选】

实现逻辑:

  1. 前端访问 GET /video/{fileId}

  2. 网关解析请求头 Range: bytes=start-end

  3. 根据「全局字节偏移量」计算:

    分片序号 = Math.floor(start / chunk_size)

    在分片内部的局部偏移 = start % chunk_size

  4. 只读取需要的单个分片局部区间,流式返回给播放器

用户拖拽进度条 → 浏览器下发新 Range → 网关快速定位对应分片,不需要读取全部前面的分片!

举例子:chunk_size=10MB,拖拽到 15MB 位置

start=1510241024

chunkNo = 1(第二个分片),分片内偏移 5MB,只读取 chunk1 从 5MB 开始的数据。

能力:完美支持拖拽 seek、进度缓冲,兼容标准 html5 video

路线 B:转 HLS 切片(适合短视频、长视频平台)

注意:这里要区分两个概念,不要混淆!

  • 存储分片(我们上传文件时固定切的块,比如 10MB):文件存储层分片
  • HLS 切片(ts 片段,5~10 秒一段):流媒体播放层切片

如果直接拿存储分片去做 HLS,会出现严重问题:

原始文件分片边界和视频关键帧不对齐 → seek 卡顿、黑屏。

标准做法:

文件上传完成后,后台异步任务调用转码服务,生成独立 HLS 索引 (.m3u8)+ts 片段,存入存储;

前端直接播放 m3u8,不再使用原始文件分片索引

适用场景:短视频平台、直播回放;

成本:需要额外转码算力。

选型建议:

内部系统、中小并发:方案 A 网关 Range 代理足够用

面向公网大量用户、长视频平台:异步转码 HLS。

在线文档预览(PDF、Word、PPT、图片)

同普通下载(网关内部流式拼接所有分片输出完整文件流。)


分片上传
http://hanqichuan.com/2026/07/31/架构与分布式/系统设计/分片上传/
作者
韩启川
发布于
2026年7月31日
许可协议