# 文件上传下载 **本文引用的文件** - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [MultipartInitDTO.java](file://crm-file/src/main/java/com/crm/file/domain/dto/MultipartInitDTO.java) - [UploadSession.java](file://crm-file/src/main/java/com/crm/file/domain/dto/UploadSession.java) - [FileDownloadDTO.java](file://crm-file/src/main/java/com/crm/file/domain/dto/FileDownloadDTO.java) - [FileInfoDTO.java](file://crm-file/src/main/java/com/crm/file/domain/dto/FileInfoDTO.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) - [FileProperties.java](file://crm-file/src/main/java/com/crm/file/config/FileProperties.java) - [SchedulingConfig.java](file://crm-file/src/main/java/com/crm/file/config/SchedulingConfig.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [application.yml](file://crm-app/src/main/resources/application.yml) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录:API 接口规范与示例](#附录api-接口规范与示例) ## 简介 本技术文档围绕文件上传下载能力,系统性阐述单文件上传、分片上传与断点续传的实现机制,覆盖 MultipartFile 处理流程、会话管理、进度跟踪、并发控制、重试与异常恢复策略,以及文件校验规则(大小限制、类型验证等)与安全机制。同时提供完整的 API 接口规范、请求响应示例和错误处理方案,并给出客户端集成建议与最佳实践。 ## 项目结构 文件模块位于 crm-file 子模块中,采用分层设计: - Controller 层:对外暴露 REST 接口,统一返回 Result 包装体 - Service 层:封装业务逻辑,协调存储与元数据服务 - Domain 层:实体与 DTO,承载上传会话、文件信息、下载参数等 - Config 层:MinIO 存储配置、文件大小限制、调度任务开关等 - Task 层:清理孤立分片的定时任务 - Base 模块:统一的异常处理、结果封装、安全工具等 ```mermaid graph TB Client["客户端"] --> FC["FileController
REST 接口"] FC --> FA["FileApi
抽象接口"] FA --> FAI["FileApiImpl
实现类"] FAI --> FS["IFileInfoService / FileInfoServiceImpl
元数据服务"] FAI --> MINIO["MinIO 存储
MinioConfig"] FAI --> TASK["OrphanChunkCleanupTask
分片清理"] FS --> DB["数据库
FileInfoMapper"] FC --> RESULT["Result / ResultCodeEnum
统一响应"] FC --> EXC["GlobalExceptionHandlerAdvice
全局异常处理"] ``` 图表来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) - [SchedulingConfig.java](file://crm-file/src/main/java/com/crm/file/config/SchedulingConfig.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) ## 核心组件 - FileController:定义上传、分片上传、断点续传、下载、进度查询等 REST 端点,统一使用 Result 包装响应 - FileApi / FileApiImpl:上传下载的核心业务编排,包含分片初始化、分片合并、元数据持久化、存储对象操作 - IFileInfoService / FileInfoServiceImpl:文件元数据的增删改查,维护 FileInfo 实体与数据库映射 - MinioConfig / FileProperties:MinIO 连接参数、桶名、访问密钥、文件大小限制、分片大小等配置 - OrphanChunkCleanupTask:定时扫描并清理长时间未完成的孤立分片,释放存储空间 - Result / ResultCodeEnum / GlobalExceptionHandlerAdvice:统一响应体、错误码、全局异常捕获与转换 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) - [FileProperties.java](file://crm-file/src/main/java/com/crm/file/config/FileProperties.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) ## 架构总览 整体采用“控制器-服务-存储”的分层架构,结合 MinIO 作为对象存储,元数据落库,分片上传通过会话管理保证可靠性与可恢复性。 ```mermaid sequenceDiagram participant C as "客户端" participant FC as "FileController" participant FA as "FileApiImpl" participant FS as "FileInfoServiceImpl" participant MINIO as "MinIO" participant DB as "数据库" C->>FC : "POST /upload/init (分片初始化)" FC->>FA : "initMultipart(params)" FA->>MINIO : "创建分片桶/命名空间" FA-->>C : "返回 uploadId/sessionId" loop "分片上传" C->>FC : "POST /upload/chunk (携带分片数据)" FC->>FA : "uploadChunk(uploadId, chunkIndex, data)" FA->>MINIO : "PUT Object(分片)" FA-->>C : "返回分片状态" end C->>FC : "POST /upload/complete (完成合并)" FC->>FA : "completeMultipart(uploadId, chunks)" FA->>MINIO : "合并分片为完整对象" FA->>FS : "保存 FileInfo 元数据" FS->>DB : "INSERT/UPDATE FileInfo" FA-->>C : "返回文件ID/URL" C->>FC : "GET /download/{fileId}" FC->>FA : "download(fileId)" FA->>FS : "查询 FileInfo" FS->>DB : "SELECT FileInfo" FA->>MINIO : "GetObject(流式下载)" FA-->>C : "返回文件流" ``` 图表来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) ## 详细组件分析 ### 单文件上传流程(MultipartFile) - 入口:FileController 接收 MultipartFile 参数 - 校验:基于 FileProperties 的大小限制与类型白名单进行校验 - 存储:调用 FileApiImpl 将文件写入 MinIO,生成唯一对象键 - 元数据:FileInfoServiceImpl 持久化文件元数据(名称、大小、类型、路径等) - 响应:统一 Result 包装成功或错误信息 ```mermaid flowchart TD Start(["开始"]) --> Validate["校验文件大小/类型"] Validate --> |通过| SaveToMinIO["写入 MinIO"] Validate --> |失败| Error["返回错误码"] SaveToMinIO --> PersistMeta["持久化 FileInfo"] PersistMeta --> Return["返回 Result{fileId,url}"] Error --> End(["结束"]) Return --> End ``` 图表来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [FileProperties.java](file://crm-file/src/main/java/com/crm/file/config/FileProperties.java) 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [FileProperties.java](file://crm-file/src/main/java/com/crm/file/config/FileProperties.java) ### 分片上传与断点续传 - 初始化:客户端调用初始化接口获取 uploadId/sessionId,服务端建立分片会话 - 分片上传:客户端按序或乱序上传分片,服务端记录已上传分片索引 - 断点续传:支持重复上传同一分片,服务端幂等处理;未完成会话可继续补传 - 合并完成:所有分片上传完成后触发合并,生成最终对象并更新元数据 - 进度跟踪:可通过 session 查询各分片上传状态,计算总体进度 ```mermaid classDiagram class UploadSession { +string sessionId +string uploadId +long fileSize +int chunkSize +Set~Integer~ uploadedChunks +boolean completed +timestamp createdAt +timestamp updatedAt } class FileInfo { +string id +string name +long size +string type +string storageKey +string url +timestamp createdAt +timestamp updatedAt } class FileApiImpl { +initMultipart(params) UploadSession +uploadChunk(uploadId, index, data) boolean +completeMultipart(uploadId, chunks) FileInfo -validateChunk(index, size) boolean -mergeChunks(uploadId) void } class FileInfoServiceImpl { +saveFileInfo(info) FileInfo +getFileInfo(id) FileInfo +updateFileInfo(id, info) FileInfo } FileApiImpl --> UploadSession : "创建/维护" FileApiImpl --> FileInfoServiceImpl : "持久化元数据" FileInfoServiceImpl --> FileInfo : "CRUD" ``` 图表来源 - [UploadSession.java](file://crm-file/src/main/java/com/crm/file/domain/dto/UploadSession.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) 章节来源 - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [UploadSession.java](file://crm-file/src/main/java/com/crm/file/domain/dto/UploadSession.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) ### 下载流程 - 入口:FileController 接收 fileId 或下载参数 - 校验:检查权限与文件存在性 - 读取:从 MinIO 流式读取对象内容 - 响应:以流形式返回,设置合适的 Content-Type 与文件名 ```mermaid sequenceDiagram participant C as "客户端" participant FC as "FileController" participant FA as "FileApiImpl" participant FS as "FileInfoServiceImpl" participant MINIO as "MinIO" C->>FC : "GET /download?fileId=xxx" FC->>FA : "download(fileId)" FA->>FS : "查询 FileInfo" FS-->>FA : "返回 FileInfo" FA->>MINIO : "GetObject(storageKey)" MINIO-->>FA : "返回输入流" FA-->>C : "返回文件流" ``` 图表来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [MinioConfig.java](file://crm-file/src/main/java/com/crm/file/config/MinioConfig.java) 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) ### 会话管理与进度跟踪 - 会话模型:UploadSession 记录 sessionId、uploadId、分片集合、完成状态、时间戳 - 进度计算:根据已上传分片数量与总分片数计算百分比 - 并发控制:分片上传允许乱序到达,服务端以分片索引去重 - 超时清理:OrphanChunkCleanupTask 定期清理长时间未完成的会话与分片 ```mermaid flowchart TD Init["初始化会话"] --> Upload["上传分片"] Upload --> Track{"是否全部上传?"} Track --> |否| Upload Track --> |是| Merge["合并分片"] Merge --> Complete["标记会话完成"] Complete --> Cleanup["定时清理孤立会话"] ``` 图表来源 - [UploadSession.java](file://crm-file/src/main/java/com/crm/file/domain/dto/UploadSession.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) 章节来源 - [UploadSession.java](file://crm-file/src/main/java/com/crm/file/domain/dto/UploadSession.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) ### 文件校验与安全机制 - 大小限制:通过 FileProperties 配置最大文件大小,防止超大文件导致内存溢出 - 类型验证:白名单校验 MIME 类型或扩展名,拒绝非法类型 - 权限控制:结合 SecurityUtils 获取当前用户上下文,确保上传/下载权限 - 异常处理:GlobalExceptionHandlerAdvice 统一捕获并转换为标准错误码 章节来源 - [FileProperties.java](file://crm-file/src/main/java/com/crm/file/config/FileProperties.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) ### 分片上传的并发控制、重试与异常恢复 - 并发控制:分片上传接口无状态,支持多实例并行上传;服务端以 uploadId+chunkIndex 唯一标识分片 - 重试机制:客户端对失败分片进行指数退避重试,服务端幂等处理重复分片 - 异常恢复:网络中断后重新发起分片上传,已完成分片无需重复上传;会话超时由清理任务回收 章节来源 - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) ## 依赖关系分析 - FileController 依赖 FileApi(抽象),实际由 FileApiImpl 实现 - FileApiImpl 依赖 MinIO 存储与 FileInfoService 元数据服务 - FileInfoServiceImpl 依赖数据库 Mapper 进行持久化 - 统一响应与异常处理由 base 模块提供 ```mermaid graph LR FC["FileController"] --> FA["FileApi"] FA --> FAI["FileApiImpl"] FAI --> MINIO["MinIO"] FAI --> FS["FileInfoServiceImpl"] FS --> DB["数据库"] FC --> RESULT["Result/ResultCodeEnum"] FC --> EXC["GlobalExceptionHandlerAdvice"] ``` 图表来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) ## 性能考量 - 分片大小:合理设置分片大小(如 5MB~50MB)平衡网络开销与合并成本 - 并发度:客户端分片上传并发度建议 3~10,避免服务器资源耗尽 - 流式处理:下载使用流式 IO,避免大文件全量加载到内存 - 缓存策略:热点文件的 URL 或元数据可考虑短期缓存 - 存储优化:MinIO 桶分区与对象键前缀设计提升检索效率 [本节为通用指导,不直接分析具体文件] ## 故障排查指南 - 上传失败:检查文件大小与类型是否符合配置;查看全局异常处理器返回的错误码 - 分片丢失:确认客户端是否正确重试;检查会话是否被清理任务提前回收 - 下载失败:核对 fileId 是否存在;检查 MinIO 对象键与权限 - 进度不更新:确认分片上传接口是否成功返回;检查会话状态是否标记完成 章节来源 - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) ## 结论 本文件上传下载模块通过分层架构与 MinIO 存储实现了高可靠、可扩展的单文件与分片上传能力,配合会话管理与定时清理保障断点续传与资源回收。统一响应与异常处理提升了可观测性与可维护性。建议在生产环境合理配置分片大小、并发度与超时策略,并结合监控告警保障稳定性。 [本节为总结,不直接分析具体文件] ## 附录:API 接口规范与示例 ### 接口总览 - 单文件上传:POST /upload - 分片初始化:POST /upload/init - 分片上传:POST /upload/chunk - 分片完成:POST /upload/complete - 文件下载:GET /download - 进度查询:GET /upload/status ### 请求与响应示例 - 单文件上传 - 请求:multipart/form-data,字段 file - 响应:Result{code,msg,data:{fileId,url}} - 分片初始化 - 请求:body 包含 fileName、fileSize、chunkSize - 响应:Result{data:{sessionId,uploadId}} - 分片上传 - 请求:multipart/form-data,字段 chunkIndex、chunkData - 响应:Result{data:{uploaded:true}} - 分片完成 - 请求:body 包含 uploadId、chunks[] - 响应:Result{data:{fileId,url}} - 文件下载 - 请求:query fileId - 响应:二进制流,Content-Disposition 含文件名 - 进度查询 - 请求:query sessionId - 响应:Result{data:{progress:百分比,uploadedChunks:数量}} ### 错误处理 - 统一错误码:ResultCodeEnum 定义业务错误码 - 全局异常:GlobalExceptionHandlerAdvice 捕获异常并返回标准错误格式 - 常见错误:文件过大、类型不支持、会话不存在、分片缺失、权限不足 章节来源 - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) ### 客户端集成建议与最佳实践 - 分片大小:默认 5MB~10MB,根据网络状况调整 - 并发控制:限制并发数为 3~10,避免阻塞线程池 - 重试策略:指数退避 + 最大重试次数,区分可重试与不可重试错误 - 进度反馈:轮询进度接口,展示上传进度条 - 安全性:校验文件类型与大小,服务端二次校验,避免越权访问 - 稳定性:断网恢复后从最后一个成功分片继续上传,避免重复传输 [本节为通用指导,不直接分析具体文件]