关键在于切得准、传得稳、续得上:按固定大小分片读取字节流,携带序号、总片数、文件标识等元信息;流式读取防OOM;客户端管理状态与重试;服务端支持断点续传与校验。

直接用字节流读取视频文件并分片上传,关键不在“读得多”,而在“切得准、传得稳、续得上”。核心是把大视频按固定大小切块,每块转成字节流上传,同时带好序号、总片数、文件标识等元信息,服务端才能正确拼接。
视频文件的字节流读取与分片切分
不推荐一次性 load 全量到内存(易 OOM),应使用 RandomAccessFile 或 FileChannel 配合 ByteBuffer 流式读取。Java 示例逻辑如下:
- 通过
file.length()获取总大小,设定分片大小(如 5MB) - 循环计算起始偏移
start = i * chunkSize和结束位置end = Math.min(start + chunkSize, fileSize) - 用
raf.seek(start)定位,再用raf.read(buffer)读取实际字节数(最后一片常不足 5MB) - 将读出的有效字节封装为
byte[]或临时File,作为单个分片参与上传
分片上传的客户端逻辑封装要点
前端或 Java 客户端需统一管理状态和重试,避免裸调 HTTP。封装时重点关注:
-
唯一标识生成:对原始文件算一次 MD5(或 UUID)作为
fileId,所有分片共用,便于服务端归组 -
分片元数据携带:每个请求必须含
fileId、chunkNumber(从 1 开始)、totalChunks、chunkMd5(可选,用于校验单片完整性) - 并发与限流:限制最大并行数(如 3~5 个),配合重试机制(失败自动重传 ≤3 次)
- 进度与中断记录:上传前本地缓存已成功分片列表;中断后再次上传时先查服务端已存分片,跳过已传项
服务端接收与合并逻辑衔接
客户端上传只是第一步,服务端需配套支持才能落地:
- 接收接口保存分片到临时目录(命名建议:
{fileId}_{chunkNumber}.part),并记录数据库或 Redis 中的上传状态 - 提供
/check-upload?fileId=xxx接口,返回已上传的chunkNumber列表,支撑断点续传 - 合并阶段使用 RandomAccessFile 或 Files.write(..., StandardOpenOption.APPEND) 按序写入,避免内存加载全量
- 合并完成后校验最终文件 MD5,与客户端上传的 totalMd5 对比,确保一致性
实战中容易忽略的细节
很多项目卡在“能传但不稳定”,问题往往出在边界处理:
- 最后一片不满分片大小时,
read()返回值可能小于 buffer 长度,必须用实际读取长度构造有效byte[],否则会传入填充字节导致视频损坏 - HTTP 请求头要设
Content-Type: multipart/form-data,且分片字段名和服务端约定一致(如file字段传二进制,chunkNumber传文本) - 服务端临时目录需有写权限,且定期清理超时未合并的碎片(如 24 小时未完成则自动删除)
- 移动端或弱网环境下,建议增加分片大小自适应(如网络差时降为 2MB),或加入请求超时与连接复用控制

















