⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7519122029197508644 「天天摸鱼的java工程师」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

背景场景

在实际开发中,我们经常遇到大文件上传的需求,比如:

  • 用户上传 2GB 的视频;
  • 后台上传大文件 ZIP 数据包;
  • 文件上传过程中网络中断。

如果我们仍用传统的 MultipartFile 一次性上传,很快会遇到以下问题:

  • 前端体验差: 上传失败要重传;
  • 后端压力大: 占用大量内存;
  • 不具备扩展性: 难以支持分布式部署。

目标设计

设计一个支持以下特性的文件上传接口:

  • 分片上传: 大文件拆成小块上传;
  • 断点续传: 网络中断后从断点继续;
  • 高可用性: 支持分布式部署,不依赖本地文件;
  • 可扩展性: 支持对象存储如 MinIO、OSS;
  • CDN 加速: 支持文件分发、下载优化。

技术选型

技术项 说明
Spring Boot 主体框架
MinIO 对象存储(可兼容 OSS)
MySQL 存储文件上传状态、分片记录
Redis 控制并发、分片状态缓存(可选)
CDN 文件分发加速(如七牛、阿里云)

模块设计图

核心表结构设计

CREATE TABLE file_upload_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
file_md5 VARCHAR(64) NOT NULL,
file_name VARCHAR(255),
total_chunks INT,
uploaded_chunks INT DEFAULT 0,
is_complete BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

核心接口设计

检查文件是否已上传

@GetMapping("/upload/check")
public ResponseEntity<?> checkFile(@RequestParam String fileMd5) {
FileUploadRecord record = recordRepository.findByFileMd5(fileMd5);
if (record != null && record.getIsComplete()) {
return ResponseEntity.ok(Map.of("uploaded", true, "url", getFileUrl(fileMd5)));
}
return ResponseEntity.ok(Map.of("uploaded", false));
}

上传分片接口

@PostMapping("/upload/chunk")
public ResponseEntity<?> uploadChunk(
@RequestParam String fileMd5,
@RequestParamint chunkIndex,
@RequestParam MultipartFile filePart
) throws IOException {
// 临时保存对象名:如 fileMd5/chunkIndex
StringobjectName= String.format("upload/%s/%d.part", fileMd5, chunkIndex);

// 上传到 MinIO
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(filePart.getInputStream(), filePart.getSize(), -1)
.contentType(filePart.getContentType())
.build()
);

// 更新数据库状态
recordService.markChunkUploaded(fileMd5, chunkIndex);
return ResponseEntity.ok("Chunk uploaded");
}

合并分片并生成最终文件

@PostMapping("/upload/merge")
public ResponseEntity<?> mergeChunks(@RequestParam String fileMd5, @RequestParamint totalChunks) throws Exception {
StringfinalObjectName="upload/" + fileMd5 + ".final";

// 合并文件(使用 MinIO 的 composeObject)
List<ComposeSource> sources = newArrayList<>();
for (inti=0; i < totalChunks; i++) {
sources.add(
ComposeSource.builder()
.bucket(bucketName)
.object(String.format("upload/%s/%d.part", fileMd5, i))
.build()
);
}

minioClient.composeObject(
ComposeObjectArgs.builder()
.bucket(bucketName)
.object(finalObjectName)
.sources(sources)
.build()
);

// 标记上传完成
recordService.markComplete(fileMd5);

return ResponseEntity.ok(Map.of("url", getFileUrl(fileMd5)));
}

核心逻辑详解

为什么要用 fileMd5 作为分片唯一标识?

  • MD5 可标志文件唯一性,便于去重和断点续传;
  • 客户端可预先计算 MD5,与服务器确认哪些分片已上传。

MinIO 是如何支持分片合并的?

  • MinIO 支持 composeObject,可将多个对象合并为一个;
  • 类似于 AWS S3 的“多段上传”功能;
  • 合并操作在服务端进行,避免网络传输压力。

如果网络中断怎么办?

  • 客户端每上传一个分片记录状态;
  • 重新发起上传前调用/check查询已上传分片;
  • 只上传缺失部分,节省时间和带宽。

可扩展性设计

模块化存储接口

public interface FileStorageService {
void uploadChunk(String objectName, InputStream stream, long size);
void mergeChunks(String finalName, List<String> chunkNames);
String getFileUrl(String objectName);
}
  • 实现类可对接 OSS、MinIO、FastDFS 等;
  • 降低耦合,支持替换存储供应商。

性能与高可用优化

优化点 技术方案
上传限速 Nginx 限流、前端分批上传
接口幂等 Redis 控制 chunk 上传状态
合并耗时 可异步合并 + 任务中心
多节点上传 存储层采用对象存储,支持多节点共享

返回 CDN 地址

public String getFileUrl(String fileMd5) {
return cdnDomain + "/upload/" + fileMd5 + ".final";
}
  • 文件合并后部署到 CDN;
  • 前端访问地址统一为 CDN 加速域名。

总结

我们通过以下方案构建了一个高可用、可扩展的大文件上传接口:

  • 支持 断点续传、分片上传;
  • 使用 MinIO/S3 对象存储;
  • 支持 CDN 分发;
  • 可轻松扩展存储后端或接入云服务。

推荐工具 & 框架

  • MinIO Java SDK
  • Spring Boot + Spring Web
  • Redis(限流、幂等)
  • MySQL(记录上传状态)

结语

这套文件上传方案,特别适合对稳定性、高并发、用户体验要求较高的系统。希望本文能为你在实际项目中实现大文件上传提供思路。

文章目录
  1. 1. 背景场景
  2. 2. 目标设计
  3. 3. 技术选型
  4. 4. 模块设计图
  5. 5. 核心表结构设计
  6. 6. 核心接口设计
    1. 6.0.0.1. 检查文件是否已上传
    2. 6.0.0.2. 上传分片接口
    3. 6.0.0.3. 合并分片并生成最终文件
  • 7. 核心逻辑详解
    1. 7.0.1. 为什么要用 fileMd5 作为分片唯一标识?
    2. 7.0.2. MinIO 是如何支持分片合并的?
    3. 7.0.3. 如果网络中断怎么办?
  • 8. 可扩展性设计
    1. 8.0.1. 模块化存储接口
  • 9. 性能与高可用优化
  • 10. 返回 CDN 地址
  • 11. 总结
  • 12. 推荐工具 & 框架
  • 13. 结语