16 KiB
16 KiB
后端文件服务
**本文引用的文件** - [FileController.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/controller/FileController.java) - [FileService.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/service/FileService.java) - [FileStorageProperties.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/config/FileStorageProperties.java) - [GlobalExceptionHandler.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/GlobalExceptionHandler.java) - [BusinessException.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/BusinessException.java) - [application.yaml](file://fms-api/src/main/resources/application.yaml)目录
简介
本文件为 FMS 后端文件服务的综合文档,聚焦于文件上传、下载、删除、预览等核心能力。内容涵盖:
- FileService 核心服务类的实现与数据流
- FileController 控制器的 RESTful API 设计
- FileStorageProperties 配置类支持的存储策略(本地文件系统与云存储 OSS)
- 安全机制(访问路径校验、类型与大小限制建议、权限上下文)
- API 调用示例与错误处理策略(含事务管理与异常处理)
项目结构
后端文件相关代码位于 fms-api 模块中,主要包含控制器、服务、配置与异常处理四类文件:
- 控制器:FileController 暴露文件操作的 HTTP 端点
- 服务:FileService 封装文件上传、删除、OSS 凭证获取、记录保存等业务逻辑
- 配置:FileStorageProperties 提供本地与 OSS 的存储策略配置
- 异常:GlobalExceptionHandler 统一捕获并返回标准化错误响应
graph TB
Client["客户端"] --> Ctrl["FileController<br/>REST 接口"]
Ctrl --> Svc["FileService<br/>业务逻辑"]
Svc --> Cfg["FileStorageProperties<br/>存储配置"]
Svc --> DB["数据库<br/>bf_files"]
Svc --> FS["本地文件系统"]
Svc --> OSS["OSS 网关<br/>g3oss"]
图表来源
章节来源
核心组件
- FileController:定义 /file 命名空间下的 REST 接口,包括配置查询、本地上传、OSS 上传凭证、保存记录、删除、静态资源访问等。
- FileService:实现本地上传、OSS 凭证获取、记录保存、删除(本地/OSS)、DB 写入与清理等核心流程。
- FileStorageProperties:通过前缀 fms.file 绑定配置,支持 storageType、local.storageDir/urlPrefix、oss.g3ossUrl。
- GlobalExceptionHandler:统一捕获认证、业务、数据库及系统异常,返回 ApiResponse 标准格式。
章节来源
- FileController.java:28-112
- FileService.java:34-266
- FileStorageProperties.java:5-68
- GlobalExceptionHandler.java:15-84
架构总览
文件服务采用“控制器-服务-存储”的分层架构:
- 控制器负责参数接收与响应包装
- 服务负责业务编排与存储适配(本地/OSS)
- 配置类决定运行时存储策略
- 异常处理器统一错误输出
sequenceDiagram
participant C as "客户端"
participant Ctrl as "FileController"
participant Svc as "FileService"
participant Cfg as "FileStorageProperties"
participant DB as "数据库"
participant FS as "本地文件系统"
participant OSS as "OSS 网关"
C->>Ctrl : POST /file/upload
Ctrl->>Svc : localUpload(files, father, moduleId, cateId)
Svc->>Cfg : 读取本地存储目录与URL前缀
Svc->>FS : 写入文件到 {orgId}/{yyyy}/{MM}/{dd}/文件名
Svc->>DB : 插入 bf_files 记录
Svc-->>Ctrl : 返回文件元数据列表
Ctrl-->>C : ApiResponse.success(列表)
图表来源
详细组件分析
FileController 控制器
- 职责
- 提供 /file/config 返回当前存储模式
- 提供 /file/upload 本地多文件上传
- 提供 /file/upload-credential 获取 OSS 上传凭证
- 提供 /file/save-record 保存文件记录(OSS 直传回调)
- 提供 /file/delete 删除文件
- 提供 /file/static/{fileName} 本地静态文件访问(内联预览)
- 关键点
- 使用统一的 ApiResponse 包装成功结果
- 静态文件访问包含路径穿越防护,仅允许在配置的本地存储目录下访问
- 删除操作委托给 FileService,由服务根据存储类型选择删除策略
flowchart TD
Start(["请求进入 /file/*"]) --> Route{"路由匹配"}
Route --> |/config| GetCfg["返回 storageType"]
Route --> |/upload| Upload["调用 FileService.localUpload"]
Route --> |/upload-credential| Cred["调用 FileService.ossCredential"]
Route --> |/save-record| Save["调用 FileService.saveRecord"]
Route --> |/delete| Del["调用 FileService.delete"]
Route --> |/static/{fileName}| Serve["读取本地文件并返回"]
GetCfg --> End(["返回 ApiResponse"])
Upload --> End
Cred --> End
Save --> End
Del --> End
Serve --> End
图表来源
章节来源
FileService 服务
- 职责
- 本地上传:生成唯一文件名与子路径,落盘后写入 bf_files 表,返回元数据
- OSS 凭证:向 g3oss 网关请求上传凭证(bucket=机构码)
- 保存记录:将前端直传后的文件信息持久化到 bf_files
- 删除:根据存储类型删除本地文件或调用 OSS 网关删除,并删除 DB 记录
- 关键流程
- 本地上传:按 orgId/年/月/日 组织目录,避免冲突;URL 映射基于 urlPrefix + 相对路径
- 删除:先删存储再删 DB;OSS 删除时从 mapfilename 提取 objectName 并调用 g3oss 删除接口
- 数据库连接:通过 DataSourceUtils 获取与释放连接,确保资源正确回收
classDiagram
class FileService {
+localUpload(files, father, moduleId, cateId) Map[]
+saveRecord(meta) Map
+ossCredential() Map
+delete(subid) void
-insertFileRecord(...)
-deleteLocalFile(mapfilename)
-deleteOssFile(mapfilename)
-deleteDbRow(subid)
}
class FileStorageProperties {
+storageType : String
+local : Local
+oss : Oss
}
class DbUtils {
+loadData(table, where, cols, params) Map[]
}
FileService --> FileStorageProperties : "读取存储策略"
FileService --> DbUtils : "查询/删除记录"
图表来源
章节来源
FileStorageProperties 配置
- 配置前缀:fms.file
- 字段说明
- storageType:存储类型,如 local 或 aliyun-oss
- local.storageDir:本地文件根目录
- local.urlPrefix:本地文件访问 URL 前缀
- oss.g3ossUrl:OSS 网关地址(用于获取凭证与删除)
- 作用:驱动 FileService 在不同存储后端间切换
flowchart LR
A["application.yaml<br/>fms.file.*"] --> B["FileStorageProperties"]
B --> C["FileService<br/>选择本地/OSS"]
图表来源
章节来源
安全机制
- 访问权限验证
- 通过 OrgContext 获取当前机构与用户上下文,用于目录隔离与审计字段填充
- 文件类型过滤
- 当前未在服务层做严格白名单校验;建议在控制器或服务层增加扩展名白名单校验
- 大小限制
- 当前未在服务层做文件大小限制;建议在控制器或服务层增加大小校验
- 路径穿越防护
- 静态文件访问时对解析路径进行规范化并校验是否在允许的根目录下,防止越权访问
章节来源
API 端点规范
- GET /file/config
- 功能:返回当前存储模式
- 响应:ApiResponse<Map<String,Object>>,包含 storageType
- POST /file/upload
- 功能:本地多文件上传
- 表单字段:files(MultipartFile[])、father、moduleId、cateId(可选)
- 响应:ApiResponse<List<Map<String,Object>>>,包含 subid、文件名、大小、扩展名、映射路径等
- POST /file/upload-credential
- 功能:获取 OSS 上传凭证(bucket=机构码)
- 响应:ApiResponse,包含 OSS 上传所需参数
- POST /file/save-record
- 功能:OSS 直传后回调保存文件记录
- 请求体:包含 subid、father、mx_moduleid、mx_filename、mx_filesize、mx_fileext、mx_mapfilename 等
- 响应:ApiResponse,包含 subid 与消息
- POST /file/delete
- 功能:删除文件(本地或 OSS)
- 请求体:subid
- 响应:ApiResponse
- GET /file/static/{fileName:.+}
- 功能:本地静态文件访问(内联预览)
- 路径规则:{orgId}/{yyyy}/{MM}/{dd}/{file}
- 响应:二进制流,Content-Disposition 设置为 inline
章节来源
事务管理与异常处理
- 事务管理
- 当前数据库操作通过 DataSourceUtils 获取连接并手动释放,未显式声明事务边界;如需强一致性,可在方法上添加 @Transactional
- 异常处理
- 全局异常处理器统一捕获认证、业务、数据库与系统异常,返回 ApiResponse.fail
- 业务异常(如 OSS 调用失败)抛出 BusinessException,由全局处理器转换为统一错误响应
sequenceDiagram
participant C as "客户端"
participant Ctrl as "FileController"
participant Svc as "FileService"
participant EH as "GlobalExceptionHandler"
C->>Ctrl : 发起文件操作
Ctrl->>Svc : 执行业务
alt 发生异常
Svc-->>Ctrl : 抛出异常
Ctrl-->>EH : 触发异常处理
EH-->>C : ApiResponse.fail(错误码/消息)
else 正常
Svc-->>Ctrl : 返回结果
Ctrl-->>C : ApiResponse.success(结果)
end
图表来源
章节来源
依赖关系分析
- FileController 依赖 FileStorageProperties 与 FileService
- FileService 依赖 FileStorageProperties、DbUtils、DataSource、OrgContext
- 存储策略由 FileStorageProperties 的 storageType 决定,影响本地/OSS 分支逻辑
- 异常处理由 GlobalExceptionHandler 集中管理
graph LR
Ctrl["FileController"] --> Props["FileStorageProperties"]
Ctrl --> Svc["FileService"]
Svc --> Props
Svc --> DBU["DbUtils"]
Svc --> DS["DataSource"]
Svc --> OC["OrgContext"]
EH["GlobalExceptionHandler"] --> Ctrl
图表来源
章节来源
性能考虑
- 本地上传
- 逐个文件写入磁盘与数据库,适合中小文件;大文件建议分片或流式处理
- OSS 直传
- 减少服务端带宽压力,但需保证凭证获取与回调的稳定性
- 并发与限流
- 可结合网关或应用层限流保护上传与删除接口
- 缓存与索引
- 对频繁访问的静态资源可引入 CDN 或反向代理缓存
[本节为通用指导,不直接分析具体文件]
故障排查指南
- 常见问题
- 获取 OSS 凭证失败:检查 g3ossUrl 与网络连通性,确认返回状态码
- 删除文件失败:核对 mapfilename 与存储类型,确认本地路径或 OSS objectName 正确
- 静态文件 404:检查路径穿越防护是否拦截,确认文件存在于配置的本地目录
- 日志与定位
- 全局异常处理器会记录系统异常的请求方法与 URI,便于快速定位问题
- 业务异常会携带明确错误信息,优先查看该消息
章节来源
- GlobalExceptionHandler.java:66-84
- FileService.java:162-179
- FileService.java:213-246
- FileController.java:90-111
结论
FMS 后端文件服务以简洁的分层架构实现了本地与 OSS 两种存储策略的统一接入。FileController 提供清晰的 REST 接口,FileService 封装了核心业务逻辑与存储适配,FileStorageProperties 使配置灵活可控,GlobalExceptionHandler 保障一致的异常体验。建议在生产环境中补充文件类型白名单与大小限制,并根据业务规模评估是否需要引入事务与更完善的监控告警。
[本节为总结性内容,不直接分析具体文件]
附录
- 配置项参考(fms.file.*)
- storageType:local 或 aliyun-oss
- local.storageDir:本地文件根目录
- local.urlPrefix:本地文件访问 URL 前缀
- oss.g3ossUrl:OSS 网关地址
- 典型调用示例(描述性)
- 上传:POST /file/upload,表单字段 files、father、moduleId、cateId(可选)
- 获取凭证:POST /file/upload-credential
- 保存记录:POST /file/save-record,JSON 包含 subid、文件名、大小、扩展名、映射路径等
- 删除:POST /file/delete,JSON 包含 subid
- 预览:GET /file/static/{orgId}/{yyyy}/{MM}/{dd}/{file}
[本节为补充信息,不直接分析具体文件]