You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

22 KiB

文件上传下载

**本文引用的文件** - [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 接口规范与示例

简介

本技术文档围绕文件上传下载能力,系统性阐述单文件上传、分片上传与断点续传的实现机制,覆盖 MultipartFile 处理流程、会话管理、进度跟踪、并发控制、重试与异常恢复策略,以及文件校验规则(大小限制、类型验证等)与安全机制。同时提供完整的 API 接口规范、请求响应示例和错误处理方案,并给出客户端集成建议与最佳实践。

项目结构

文件模块位于 crm-file 子模块中,采用分层设计:

  • Controller 层:对外暴露 REST 接口,统一返回 Result 包装体
  • Service 层:封装业务逻辑,协调存储与元数据服务
  • Domain 层:实体与 DTO,承载上传会话、文件信息、下载参数等
  • Config 层:MinIO 存储配置、文件大小限制、调度任务开关等
  • Task 层:清理孤立分片的定时任务
  • Base 模块:统一的异常处理、结果封装、安全工具等
graph TB
Client["客户端"] --> FC["FileController<br/>REST 接口"]
FC --> FA["FileApi<br/>抽象接口"]
FA --> FAI["FileApiImpl<br/>实现类"]
FAI --> FS["IFileInfoService / FileInfoServiceImpl<br/>元数据服务"]
FAI --> MINIO["MinIO 存储<br/>MinioConfig"]
FAI --> TASK["OrphanChunkCleanupTask<br/>分片清理"]
FS --> DB["数据库<br/>FileInfoMapper"]
FC --> RESULT["Result / ResultCodeEnum<br/>统一响应"]
FC --> EXC["GlobalExceptionHandlerAdvice<br/>全局异常处理"]

图表来源

  • FileController.java
  • FileApi.java
  • FileApiImpl.java
  • IFileInfoService.java
  • FileInfoServiceImpl.java
  • MinioConfig.java
  • OrphanChunkCleanupTask.java
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

章节来源

  • FileController.java
  • FileApi.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • MinioConfig.java
  • SchedulingConfig.java
  • OrphanChunkCleanupTask.java
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

核心组件

  • FileController:定义上传、分片上传、断点续传、下载、进度查询等 REST 端点,统一使用 Result 包装响应
  • FileApi / FileApiImpl:上传下载的核心业务编排,包含分片初始化、分片合并、元数据持久化、存储对象操作
  • IFileInfoService / FileInfoServiceImpl:文件元数据的增删改查,维护 FileInfo 实体与数据库映射
  • MinioConfig / FileProperties:MinIO 连接参数、桶名、访问密钥、文件大小限制、分片大小等配置
  • OrphanChunkCleanupTask:定时扫描并清理长时间未完成的孤立分片,释放存储空间
  • Result / ResultCodeEnum / GlobalExceptionHandlerAdvice:统一响应体、错误码、全局异常捕获与转换

章节来源

  • FileController.java
  • FileApi.java
  • FileApiImpl.java
  • IFileInfoService.java
  • FileInfoServiceImpl.java
  • MinioConfig.java
  • FileProperties.java
  • OrphanChunkCleanupTask.java
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

架构总览

整体采用“控制器-服务-存储”的分层架构,结合 MinIO 作为对象存储,元数据落库,分片上传通过会话管理保证可靠性与可恢复性。

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
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • MinioConfig.java

详细组件分析

单文件上传流程(MultipartFile)

  • 入口:FileController 接收 MultipartFile 参数
  • 校验:基于 FileProperties 的大小限制与类型白名单进行校验
  • 存储:调用 FileApiImpl 将文件写入 MinIO,生成唯一对象键
  • 元数据:FileInfoServiceImpl 持久化文件元数据(名称、大小、类型、路径等)
  • 响应:统一 Result 包装成功或错误信息
flowchart TD
Start(["开始"]) --> Validate["校验文件大小/类型"]
Validate --> |通过| SaveToMinIO["写入 MinIO"]
Validate --> |失败| Error["返回错误码"]
SaveToMinIO --> PersistMeta["持久化 FileInfo"]
PersistMeta --> Return["返回 Result{fileId,url}"]
Error --> End(["结束"])
Return --> End

图表来源

  • FileController.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • FileProperties.java

章节来源

  • FileController.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • FileProperties.java

分片上传与断点续传

  • 初始化:客户端调用初始化接口获取 uploadId/sessionId,服务端建立分片会话
  • 分片上传:客户端按序或乱序上传分片,服务端记录已上传分片索引
  • 断点续传:支持重复上传同一分片,服务端幂等处理;未完成会话可继续补传
  • 合并完成:所有分片上传完成后触发合并,生成最终对象并更新元数据
  • 进度跟踪:可通过 session 查询各分片上传状态,计算总体进度
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
  • FileInfo.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java

章节来源

  • FileApiImpl.java
  • UploadSession.java
  • FileInfo.java
  • FileInfoServiceImpl.java

下载流程

  • 入口:FileController 接收 fileId 或下载参数
  • 校验:检查权限与文件存在性
  • 读取:从 MinIO 流式读取对象内容
  • 响应:以流形式返回,设置合适的 Content-Type 与文件名
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
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • MinioConfig.java

章节来源

  • FileController.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java

会话管理与进度跟踪

  • 会话模型:UploadSession 记录 sessionId、uploadId、分片集合、完成状态、时间戳
  • 进度计算:根据已上传分片数量与总分片数计算百分比
  • 并发控制:分片上传允许乱序到达,服务端以分片索引去重
  • 超时清理:OrphanChunkCleanupTask 定期清理长时间未完成的会话与分片
flowchart TD
Init["初始化会话"] --> Upload["上传分片"]
Upload --> Track{"是否全部上传?"}
Track --> |否| Upload
Track --> |是| Merge["合并分片"]
Merge --> Complete["标记会话完成"]
Complete --> Cleanup["定时清理孤立会话"]

图表来源

  • UploadSession.java
  • OrphanChunkCleanupTask.java

章节来源

  • UploadSession.java
  • OrphanChunkCleanupTask.java

文件校验与安全机制

  • 大小限制:通过 FileProperties 配置最大文件大小,防止超大文件导致内存溢出
  • 类型验证:白名单校验 MIME 类型或扩展名,拒绝非法类型
  • 权限控制:结合 SecurityUtils 获取当前用户上下文,确保上传/下载权限
  • 异常处理:GlobalExceptionHandlerAdvice 统一捕获并转换为标准错误码

章节来源

  • FileProperties.java
  • SecurityUtils.java
  • GlobalExceptionHandlerAdvice.java

分片上传的并发控制、重试与异常恢复

  • 并发控制:分片上传接口无状态,支持多实例并行上传;服务端以 uploadId+chunkIndex 唯一标识分片
  • 重试机制:客户端对失败分片进行指数退避重试,服务端幂等处理重复分片
  • 异常恢复:网络中断后重新发起分片上传,已完成分片无需重复上传;会话超时由清理任务回收

章节来源

  • FileApiImpl.java
  • OrphanChunkCleanupTask.java

依赖关系分析

  • FileController 依赖 FileApi(抽象),实际由 FileApiImpl 实现
  • FileApiImpl 依赖 MinIO 存储与 FileInfoService 元数据服务
  • FileInfoServiceImpl 依赖数据库 Mapper 进行持久化
  • 统一响应与异常处理由 base 模块提供
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
  • FileApi.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

章节来源

  • FileController.java
  • FileApi.java
  • FileApiImpl.java
  • FileInfoServiceImpl.java
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

性能考量

  • 分片大小:合理设置分片大小(如 5MB~50MB)平衡网络开销与合并成本
  • 并发度:客户端分片上传并发度建议 3~10,避免服务器资源耗尽
  • 流式处理:下载使用流式 IO,避免大文件全量加载到内存
  • 缓存策略:热点文件的 URL 或元数据可考虑短期缓存
  • 存储优化:MinIO 桶分区与对象键前缀设计提升检索效率

[本节为通用指导,不直接分析具体文件]

故障排查指南

  • 上传失败:检查文件大小与类型是否符合配置;查看全局异常处理器返回的错误码
  • 分片丢失:确认客户端是否正确重试;检查会话是否被清理任务提前回收
  • 下载失败:核对 fileId 是否存在;检查 MinIO 对象键与权限
  • 进度不更新:确认分片上传接口是否成功返回;检查会话状态是否标记完成

章节来源

  • GlobalExceptionHandlerAdvice.java
  • OrphanChunkCleanupTask.java
  • 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
  • Result.java
  • ResultCodeEnum.java
  • GlobalExceptionHandlerAdvice.java

客户端集成建议与最佳实践

  • 分片大小:默认 5MB~10MB,根据网络状况调整
  • 并发控制:限制并发数为 3~10,避免阻塞线程池
  • 重试策略:指数退避 + 最大重试次数,区分可重试与不可重试错误
  • 进度反馈:轮询进度接口,展示上传进度条
  • 安全性:校验文件类型与大小,服务端二次校验,避免越权访问
  • 稳定性:断网恢复后从最后一个成功分片继续上传,避免重复传输

[本节为通用指导,不直接分析具体文件]