分片上传
分片上传
分片大小
不能太小:分片数量暴增,元数据压力大、请求次数多;
不能太大:网络中断后重复传输成本高;
使用固定大小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,线上最常用)
流程:
- 前端调用业务后端:initUpload,获取 uploadId
- 后端生成 UploadPart 预签名 URL 返回前端
- 浏览器直接把分片 POST 上传到 OSS/MinIO,不经过业务服务
- 分片全部传完,前端请求后端执行 Complete
巨大限制(重点)
分片请求直连存储,绕过你的应用服务!
你无法在服务端实现【任务级滑动窗口 in_flight 限流】
所有分片请求不经过网关,没法拦截超限并发
无法统一拦截重复分片、实时校验分片 MD5
只能依靠前端滑动窗口做并发控制,客户端 bug 会直接打满存储
自定义分片不合并方案的下载
普通下载
网关流式拼接下载
请求链路:
浏览器 → 自研文件网关 → 查询 DB 分片索引 → 循环并行 / 串行读取各个分片 → 按顺序流式输出 Response
特点:
- 用户侧 URL:
GET /download/{fileId},和普通文件无差别;不用改前端 - 网关不落地完整文件,内存流式转发,不占用磁盘
网关返回分片清单,客户端本地拼接
网关返回 JSON:分片地址列表、分片大小。
客户端并行下载所有分片,本地组装成完整文件。
视频在线播放
网页 <video src="xxx">、HLS/MPD 点播,底层依赖 HTTP Range 分片请求、流式输出、支持 seek 拖拽。
原生 video 标签只能访问单个资源地址,无法直接读取一堆分片。
提供两套落地路线:
路线 A:网关代理 + 支持 Range 请求【Web 首选】
实现逻辑:
前端访问
GET /video/{fileId}网关解析请求头
Range: bytes=start-end根据「全局字节偏移量」计算:
分片序号 = Math.floor(start / chunk_size)在分片内部的局部偏移 = start % chunk_size
只读取需要的单个分片局部区间,流式返回给播放器
用户拖拽进度条 → 浏览器下发新 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、图片)
同普通下载(网关内部流式拼接所有分片输出完整文件流。)