5.0 KiB
5.0 KiB
File Upload FileId Design
Date: 2026-03-10
Goal
为 TMS 提供一个通用文件上传能力,前端在初始化页面选择文件后先上传,后端返回 fileId,初始化任务创建和执行阶段继续只引用 fileId,不直接传输文件流。
Scope
本设计只覆盖:
- 通用文件上传接口
- 文件元信息记录
- 与 CISD 初始化的
fileId约定 - 前端调用时序
本设计不覆盖:
- 文件下载
- 文件删除回收
- 秒传/去重
- 文件内容级业务校验
- 初始化主流程改造
Current State
当前初始化接口:
/api/v1/init/preview/api/v1/init/tasks
都只接收 JSON InitPreviewRequest,不接收 multipart/form-data。
当前初始化模块已经完整依赖以下 fileId 字段:
licenses.receiverLicenseFileIdlicenses.cfgZipFileIdmq.tlqLicenseFileIdmq.cfmqConfigFileId
当前执行器通过 upload-base-dir/<fileId> 或 upload-base-dir/<fileId>/<preferredFileName> 解析文件。
Design Choice
采用:通用上传接口 + 初始化继续引用 fileId
不采用:初始化任务接口直接接收 MultipartFile
原因:
- 初始化主流程已经基于
fileId,继续沿用改动最小 - 上传与初始化创建失败语义不同,拆分更清晰
- 通用上传能力后续可被证书、升级、备份等模块复用
API Design
1. Upload API
POST /api/v1/files/upload
Content-Type:multipart/form-data
表单字段:
file: 必填,文件本体bizType: 可选,默认GENERIC
返回示例:
{
"success": true,
"code": 200,
"msg": "success",
"data": {
"fileId": "f_20260310_ab12cd34",
"bizType": "INIT",
"originalFilename": "cmep.license",
"contentType": "application/octet-stream",
"size": 2048,
"storagePath": "/home/tms/uploads/f_20260310_ab12cd34/cmep.license"
},
"timestamp": 1741600000000,
"traceId": "xxx"
}
2. File Detail API
GET /api/v1/files/{fileId}
用于页面回显和排障,不参与初始化主链路。
Storage Design
Storage root
/home/tms/uploads
Directory layout
/home/tms/uploads/<fileId>//home/tms/uploads/<fileId>/<originalFilename>
这样做的原因:
- 与当前执行器解析逻辑兼容
- 保留原始文件名,便于排查
- 每个
fileId单独目录,避免同名覆盖
Database Design
新增表:sys_file_record
字段建议:
idbigint pkfile_idvarchar(128) unique not nullbiz_typevarchar(32) not nulloriginal_filenamevarchar(256) not nullstored_filenamevarchar(256) not nullstorage_pathvarchar(512) not nullcontent_typevarchar(128) nullfile_sizebigint not nullsha256varchar(64) nullstatusvarchar(16) not nullcreate_timedatetime not nullupdate_timedatetime not null
状态值当前只需要:
ACTIVEDELETED
Backend Module Layout
新增模块:
src/main/java/com/cisd/tms/modules/file
├── controller/internal
│ └── FileInternalController.java
├── dto
│ ├── FileUploadResponse.java
│ └── FileDetailResponse.java
├── entity
│ └── FileRecordEntity.java
├── repository
│ └── FileRecordRepository.java
└── service
└── FileService.java
资源文件:
src/main/resources/mapper/file/FileRecordMapper.xml
src/main/resources/db/migration/V3__create_sys_file_record.sql
Validation Boundary
Upload API validates
- 文件非空
- 原始文件名非空
- 文件大小不超过配置上限
bizType枚举合法
Init API continues to validate
fileId格式- 按版本和
mqType的必填规则 - 执行阶段按
fileId是否能找到对应文件
Frontend Flow
- 用户在初始化页面选择文件
- 前端立即调用上传接口
- 上传成功后拿到
fileId - 前端将
fileId保存到表单状态 - 用户点击“预检”或“创建任务”时,仅提交 JSON
字段映射:
- 收发器 license ->
licenses.receiverLicenseFileId - TLQ 配置/许可证 ->
mq.tlqLicenseFileId - CFMQ 配置 ->
mq.cfmqConfigFileId - 直参 cfg 包 ->
licenses.cfgZipFileId
Configuration
建议新增:
tms:
file:
upload-base-dir: /home/tms/uploads
max-file-size-mb: 50
后续可考虑让初始化执行器复用同一配置根,而不是再单独维护一份路径配置。
Risks
- 当前初始化执行器已有
tms.init.executor.upload-base-dir,如果新增tms.file.upload-base-dir而两者不一致,会导致上传成功但执行阶段找不到文件。 - 如果上传接口只保留原始文件名而不做额外清洗,需要限制危险文件名字符。
- 目前不做业务文件类型校验,上传成功不代表该文件一定适合初始化使用。
Recommendation
先实现最小闭环:
- 上传接口
- 查询接口
- 文件元信息表
- 与初始化
fileId流程对齐
不在第一版加入:
- 下载
- 删除
- 清理任务
- 哈希去重
- 扩展名白名单