跳转至

06-文件管理 bams-file

📄 创建: ZCode AI 2026-08-14 · 修改: JimWb 2026-08-14

https://doc.xpku.com/raw/tech/backend/bams-sdk/06-bams-file.md

bams-file(61 个类)提供文件存储管理能力。当前主线是 StorageProvider 体系(支持本地 + RustFS 对象存储),旧版 FileClient 策略体系作为保留能力留存。


⚠️ 重要:两套存储体系

模块内存在两套并存的存储体系:

体系 状态 说明
StorageProvider(新版/主推) ✅ 当前生效 RustFSFileController + SysFileStorageConfigController 使用。支持本地 + RustFS(S3兼容)。2026-07-12 引入
FileClient(旧版/保留) ⚠️ 孤儿代码 策略+工厂+5种后端(DB/Local/FTP/SFTP/S3),但已无 Controller 入口。2026-07-22 删除了入口 Controller

本文档以新版 StorageProvider 为主线,旧版在末尾简要说明。


一、StorageProvider 体系(主线)

1.1 架构

RustFSFileController (REST 接口)
StorageManager (门面 Facade,@Component)
        │ resolve(key) 按 key 前缀路由
        │ stripRoutingKey() 剥路由前缀
StorageProvider (接口)
   ├── LocalStorageProvider  (本地磁盘)
   └── RustFSStorageProvider (S3 兼容对象存储)

1.2 StorageProvider 接口

cn.cnbm.bams.file.storage.StorageProvider

统一的流式存储抽象,基于 InputStream

方法签名 功能
String upload(String key, InputStream, long contentLength, String contentType, String originalFilename) 上传,返回存储 key
boolean delete(String key) 删除
InputStream download(String key) 下载(调用方自行关流)
String getPresignedUrl(String key, int expireMinutes) 预签名 URL
boolean exists(String key) 是否存在
List<FileEntry> list(String prefix) 列文件

FileEntry 内部类:bucket / key / size / lastModified / storageType / originalFilename

1.3 两个实现

LocalStorageProvider

cn.cnbm.bams.file.storage.impl.LocalStorageProvider

配置项 默认值 说明
localPath /data/upload 存储根目录
localPrefix /statics URL 访问前缀

特点: - 目录布局 {localPath}/{key} - 原始文件名用 sidecar .meta 文件保存(<filename>.meta 存原名文本) - resolveSafe() 路径穿越防护(非法 key 抛 BussinessException) - getPresignedUrl 返回 localPrefix + "/" + key(本地无真正签名)

RustFSStorageProvider

cn.cnbm.bams.file.storage.impl.RustFSStorageProvider

RustFS 本质是 S3 兼容的对象存储(类似 MinIO 定位),用 AWS SDK v2 访问。纯 S3 兼容,换 MinIO/OSS/COS 同样可用。

配置项 默认值 说明
endpoint S3 端点
bucket 桶名
accessKey / secretKey 凭证
region cn-north-1 区域
pathStyle true 路径风格访问

特点: - 构造时 ensureBucket():自动探测/创建桶 - 原始文件名用 S3 Object Tagging 保存(x-amz-tagging: original-filename=<URL编码>) - list() 用 ListObjectsV2 分页(maxKeys=1000) - getPresignedUrl 用 S3Presigner 按分钟过期

1.4 StorageManager(核心门面)

cn.cnbm.bams.file.storage.StorageManager@Component

所有 Controller 只认它。特点:

能力 说明
路由机制 按上传 key 的第一段路径匹配。先查桶编码 → 再查路由键 → 默认兜底
双写 storageType=RUSTFSdualWrite=true 时,同时写 RustFS 和本地(降级副本)
生命周期 @PostConstruct init()reload()@PreDestroy 关闭 RustFS 客户端

公开 API:

方法 功能
upload(key, in, size, contentType, 原名) 上传
uploadDualWrite(key, rustfs流, 本地流, ...) 双写上传
download(key) / download(key, routeKey) 下载
delete(key) 删除(RustFS+本地都删)
list(prefix) 列文件
getOriginalFilename(key) 取原始文件名
getPresignedUrl(key, expireMinutes) 预签名 URL
getActiveConfig() 当前生效配置
reload() 重新加载配置

1.5 配置表 SysFileStorageConfig

cn.cnbm.bams.file.pojo.file.SysFileStorageConfig(表 pub_file_storage_config

字段 类型 说明
storageType String LOCALRUSTFS
rustfsEndpoint String RustFS 端点
rustfsAccessKey / rustfsSecretKey String 凭证
rustfsBucket String 桶名
rustfsRegion String 区域
rustfsPathStyle Boolean 路径风格
localPath String 本地存储路径
localPrefix String 本地 URL 前缀
dualWrite Boolean 是否双写
enabled Boolean 是否启用
routeKey String 路由键(空串=默认兜底)
corpCode String 公司编码(多租户)

1.6 上传/下载完整流程

上传 POST /file/rustfs/upload

1. 校验文件非空
2. storageManager.getActiveConfig() 取生效配置
3. 拼 key:{bizType}/{yyyyMMdd}/{UUID}{ext}(bizType 默认 files)
4. 若 RUSTFS + dualWrite → uploadDualWrite()
   否则 → upload()
5. 返回 {key, fileName, size, storageType, url(预签名60分钟)}

下载 GET /file/rustfs/download/{*key}

storageManager.download(key) → 流拷贝到 response
文件名优先用 getOriginalFilename()(本地读 .meta / RustFS 读 tagging)

其他接口

接口 方法 功能
/file/rustfs/upload POST 上传
/file/rustfs/download/{*key} GET 下载
/file/rustfs/delete POST 删除
/file/rustfs/list GET 列文件
/file/rustfs/page POST 分页(内存分页)
/file/rustfs/preview/{*key} GET 预览(POI 把 ppt 每页渲染成 base64 PNG)
/file/rustfs/config GET 当前生效配置
/file/storageConfig/reload POST 重载 StorageManager
/file/storageConfig/testConnection POST 测试 RustFS 连通

SysFileStorageConfigController 继承 EnergyBaseController,提供存储配置的 CRUD。


二、FileClient 策略体系(旧版/保留能力)

⚠️ 当前无 Controller 入口。设计本身是教科书级的策略+工厂+模板方法,源码保留完好,但 2026-07-22 删除了入口 Controller 后成为孤儿。了解即可,不要在新代码中使用

设计模式

FileClient (接口)
AbstractFileClient<Config> (模板方法基类)
    ├── DBFileClient      (数据库存储)
    ├── LocalFileClient   (本地磁盘)
    ├── FtpFileClient     (FTP)
    ├── SftpFileClient    (SFTP)
    └── S3FileClient      (S3 兼容,支持 MinIO/OSS/COS 等)

FileClientFactory (工厂,反射创建实例)
FileStorageEnum (注册表,5 种类型)

FileStorageEnum 枚举

枚举 storage 客户端类 支持桶操作
DB 1 DBFileClient ❌ 空实现
LOCAL 10 LocalFileClient ❌ 空实现
FTP 11 FtpFileClient ❌ 空实现
SFTP 12 SftpFileClient ❌ 空实现
S3 20 S3FileClient ✅ 真正实现

只有 S3FileClient 真正实现了桶操作(createBucket),其他四种的桶方法是空实现。

FileAutoConfiguration

@Configuration(proxyBeanMethods = false),只装配一个 Bean:FileClientFactory。仅服务于旧体系。


三、Excel 转换器

⚠️ 半成品:DictConvert 和 AreaConvert 的实际查询逻辑被注释/TODO 掉,目前只有 MoneyConvert/JsonConvert 可直接用。详见 12-FAQ与注意事项

基于 FastExcel(原 EasyExcel)的 Converter 实现,用于 Excel 导入导出的字段转换。

转换器 功能 状态
MoneyConvert 金额 分→元(导出时 /100 保留 2 位) ✅ 可用
JsonConvert 对象→JSON 串(导出) ✅ 可用
DictConvert 字典 label↔value(配合 @DictFormat ⚠️ 查询逻辑被 TODO 注释
AreaConvert 地区名→编号 ⚠️ 解析逻辑被注释

注解

注解 说明
@DictFormat("字典类型") 字段级,配合 DictConvert 指定字典类型
@ExcelColumnSelect 字段级,给 Excel 列加下拉(dictType 或 functionName 二选一)

四、其他组件

FileTypeUtils

cn.cnbm.bams.file.service.core.utils.FileTypeUtils

基于 Apache Tika 的 MIME 类型识别。

方法 功能
getMineType(byte[] data) / getMineType(String name) 识别 MIME 类型
getExtension(String mimeType) mimeType→后缀
writeAttachment(response, ...) 写 HTTP 附件响应(含 video Content-Range 兼容)

数据表

实体 说明
pub_file FileDO 文件记录
pub_file_config FileConfigDO 文件配置(旧版)
pub_file_content FileContentDO DB 存储内容(旧版)
pub_file_storage_config SysFileStorageConfig 存储配置(新版)

相关文档