SpringBoot整合FastDFS与Redis:实战大文件分片上传的完整解决方案 1. 为什么需要大文件分片上传在日常开发中我们经常会遇到需要上传大文件的场景比如视频网站、云盘、设计协作平台等。如果直接将大文件一次性上传会遇到几个典型问题首先是内存压力。传统上传方式会将整个文件加载到内存中当文件达到GB级别时服务端内存会被迅速耗尽。我曾在项目中遇到过上传2GB视频导致服务器OOM崩溃的情况。其次是网络稳定性。大文件上传耗时较长网络波动可能导致上传中断。想象一下上传到99%时突然断网用户不得不重新开始的糟糕体验。最后是用户体验。没有进度提示的长时间等待会让用户感到焦虑。我曾用Chrome开发者工具模拟慢速网络一个500MB文件上传需要40分钟期间浏览器完全卡死。分片上传技术将大文件切割成多个小块比如每片5MB通过以下方式解决上述问题内存优化每次只处理一个小分片断点续传记录已上传分片中断后可恢复进度显示实时计算已完成比例并行加速可同时上传多个分片2. 技术架构设计2.1 整体架构我们的解决方案采用前后端分离架构前端 - SpringBoot - Redis - FastDFS前端使用simple-uploader.js处理分片后端用SpringBoot整合Redis和FastDFS。这种组合在实践中表现出色前端simple-uploader.js 提供分片、断点、并发控制后端SpringBoot 作为统一入口Redis存储分片状态和文件元数据FastDFS分布式文件存储2.2 核心流程分片准备前端计算文件MD5按2MB分片状态检查查询Redis判断分片是否已上传分片上传逐个上传未完成的分片合并文件所有分片完成后触发合并清理缓存删除Redis中的临时状态2.3 关键技术选型对比技术优势适用场景FastDFS分布式扩展性强适合海量小文件视频、图片等静态资源存储Redis读写性能极高支持过期时间分片状态管理等临时数据simple-uploader支持断点续传、并行上传、进度显示需要良好上传体验的Web应用3. 环境准备与配置3.1 依赖安装首先在pom.xml中添加必要依赖!-- FastDFS客户端 -- dependency groupIdcom.github.tobato/groupId artifactIdfastdfs-client/artifactId version1.27.2/version /dependency !-- Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency3.2 FastDFS配置创建fdfs_client.conf配置文件connect_timeout 5 network_timeout 30 charset UTF-8 tracker_server 192.168.1.100:22122关键参数说明tracker_server配置多个Tracker可实现高可用network_timeout根据网络状况调整超时时间生产环境建议配置connection_pool.enabledtrue启用连接池3.3 Redis配置application.yml中添加spring: redis: host: 127.0.0.1 port: 6379 timeout: 3000 lettuce: pool: max-active: 8 max-wait: -1ms4. 核心代码实现4.1 文件分片上传接口PostMapping(/upload) public ResponseEntity? uploadChunk( RequestParam(file) MultipartFile file, RequestParam(chunkNumber) int chunkNumber, RequestParam(totalChunks) int totalChunks, RequestParam(identifier) String fileMd5) { // 1. 检查是否是第一个分片 if(chunkNumber 1) { // 创建FastDFS文件并保存路径到Redis StorePath path storageClient.uploadFile(..., file.getInputStream()); redisTemplate.opsForValue().set(file:fileMd5, path.getFullPath()); } else { // 2. 追加方式写入后续分片 String filePath redisTemplate.opsForValue().get(file:fileMd5); storageClient.appendFile(..., filePath, file.getInputStream()); } // 3. 更新分片状态 redisTemplate.opsForHash().put(chunk:fileMd5, String.valueOf(chunkNumber), 1); // 4. 检查是否全部完成 if(isUploadComplete(fileMd5, totalChunks)) { return ResponseEntity.ok().body(上传完成); } return ResponseEntity.ok().body(分片上传成功); }4.2 分片状态检查private boolean isUploadComplete(String fileMd5, int totalChunks) { Long count redisTemplate.opsForHash().size(chunk:fileMd5); return count ! null count totalChunks; }4.3 Redis工具类封装Component public class RedisUtil { Autowired private RedisTemplateString, Object redisTemplate; public void setChunkStatus(String fileMd5, int chunkNumber) { redisTemplate.opsForHash().put( chunk: fileMd5, String.valueOf(chunkNumber), 1); } public boolean isChunkUploaded(String fileMd5, int chunkNumber) { return redisTemplate.opsForHash() .get(chunk:fileMd5, String.valueOf(chunkNumber)) ! null; } }5. 前端实现要点5.1 simple-uploader配置new Uploader({ target: /api/upload, chunkSize: 2 * 1024 * 1024, // 2MB分片 testChunks: true, // 开启分片检查 query: { // 附加参数 }, headers: { X-Token: your_token } });5.2 文件MD5计算使用spark-md5库计算文件指纹function calculateMD5(file) { return new Promise((resolve) { const spark new SparkMD5.ArrayBuffer(); const reader new FileReader(); reader.onload e { spark.append(e.target.result); resolve(spark.end()); }; reader.readAsArrayBuffer(file); }); }5.3 断点续传实现uploader.on(fileAdded, async (file) { file.uniqueIdentifier await calculateMD5(file.file); // 检查服务器是否存在该文件 const res await fetch(/api/check?md5${file.uniqueIdentifier}); if(res.data.exist) { // 秒传逻辑 } else if(res.data.chunks) { // 断点续传标记已上传分片 uploader.markChunksUploaded(file, res.data.chunks); } });6. 生产环境优化建议6.1 性能调优FastDFS参数优化# 增加连接池大小 fdfs.pool.max-total50 # 启用连接池测试 fdfs.pool.test-while-idletrueRedis内存控制设置合理的过期时间如24小时使用Hash结构存储分片状态节省内存分片大小选择内网5-10MB公网1-2MB移动端0.5-1MB6.2 异常处理常见的坑和解决方案Tracker节点找不到检查store_lookup2配置确保防火墙开放22122端口分片丢失问题实现Redis持久化增加超时重试机制内存泄漏try (InputStream is file.getInputStream()) { // 使用try-with-resources确保流关闭 }6.3 监控方案建议监控以下指标上传成功率平均上传耗时FastDFS存储空间Redis内存使用量Prometheus配置示例- job_name: upload_service metrics_path: /actuator/prometheus static_configs: - targets: [192.168.1.100:8080]7. 完整项目结构src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── config/ # 配置类 │ │ ├── controller/ # 控制器 │ │ ├── service/ # 业务逻辑 │ │ ├── util/ # 工具类 │ │ └── Application.java │ └── resources/ │ ├── static/ # 前端页面 │ ├── application.yml # 应用配置 │ └── fdfs_client.conf # FastDFS配置关键类说明FileController处理上传请求FastDFSService封装FastDFS操作RedisUploadService管理上传状态FileValidator文件校验工具在真实项目中这套方案成功支持了日均10万的视频上传最关键的Redis分片状态管理在高峰期也保持了毫秒级响应。