Skip to content

文件存储 PRD

文档信息

项目内容
产品名称PMForge - 文件存储
文档版本v2.0
创建日期2026-09-17
最后更新2026-09-24
文档状态评审中
优先级P0

一、功能概述

1.1 功能定位

文件存储是 PMForge 平台的"文件仓库管理员"——所有需要上传、保存、下载、预览文件的业务场景(用户头像、商品图片、合同附件、导入 Excel 等),都通过它来统一管理。

它的核心价值在于**"一次接入、处处可用"**:运维人员只需配置好一种存储方案(比如阿里云 OSS),之后所有业务模块上传文件时都会自动存到 OSS 上,开发者不需要在每个模块里单独写上传逻辑。

同时支持 5 种存储方式——数据库、本地磁盘、FTP、SFTP、S3 兼容对象存储——覆盖了从"小型部署不想额外买存储"到"大规模生产用云存储"的全部场景。

1.2 目标用户

用户类型核心诉求典型操作
运维/系统管理员配置好存储方案,确保文件存得稳、取得到新建 S3 配置 → 测试连通 → 设为主配置
后台管理员查看和管理系统中已有的文件,清理过期资源按时间筛选 → 预览确认 → 批量删除
后台开发者在业务代码中调用上传/下载接口,不关心底层存在哪调用 uploadFile()getFileUrl()
前台用户上传头像、业务图片,下载所需附件点击上传 → 选择文件 → 看到预览

1.3 业务价值

  • 降低开发成本:业务模块不需要关心文件存在本地还是云端,调用统一接口即可,换存储方案时业务代码零改动
  • 节省服务器带宽:大文件通过"预签名 URL"直传到云存储,不经过后端服务器,100 个用户同时上传 50MB 文件也不会把服务器带宽打满
  • 灵活切换存储方案:开发环境用本地磁盘(零成本),测试环境用 MinIO(自建对象存储),生产环境用阿里云 OSS——只需在界面上切换配置,不改代码
  • 多租户安全隔离:每个租户的文件互不可见,数据不串台
  • 文件全生命周期可追溯:谁上传的、什么时候上传的、存在哪个配置下,都有记录可查

1.4 功能范围

功能分类后台管理端前台用户端优先级
文件列表查看与搜索P0
文件详情查看P0
文件删除(单个/批量)P0/P1
文件预览与下载P0
后端上传文件(小文件)P0
前端直传(预签名 URL,大文件)P0
存储配置管理(增删改查)P0
设置主配置P0
配置连通性测试P0
头像/图片上传P0

二、用户场景

2.1 用户角色

角色描述核心诉求
运维工程师小陈负责服务器和基础设施配置快速配好存储方案,上线前验证通过
后台管理员小丽管理系统资源和数据方便地查看、清理文件,释放存储空间
后台开发者小韩负责业务模块的后端开发调用简单接口完成文件上传下载,不关心底层
前台用户小林使用系统的普通用户快速上传头像和业务图片,流畅不卡顿

2.2 使用场景

场景1:运维工程师配置阿里云 OSS 存储

  • 用户:运维工程师小陈
  • 前置条件:已在阿里云开通 OSS 服务,创建了 Bucket,拿到了 AccessKey 和 AccessSecret
  • 操作步骤
    1. 登录后台 → 进入"基础设施 → 文件存储 → 存储配置"页面
    2. 点击"新增"按钮 → 弹出配置表单
    3. 存储器类型选择"S3 兼容对象存储"
    4. 填写配置名:"阿里云 OSS - 生产环境"
    5. 填写 endpoint:oss-cn-hangzhou.aliyuncs.com
    6. 填写 bucket:pmforge-prod
    7. 填写 accessKey 和 accessSecret
    8. "是否公开访问"选"是"(图片需要外链直接访问)
    9. 点击"保存"
    10. 回到列表,点击该配置的"测试"按钮
    11. 系统提示"测试成功",并展示测试文件的访问 URL
    12. 点击"设为主配置" → 确认
  • 期望:配置完成后,所有新上传的文件自动存到阿里云 OSS
  • 异常处理
    • 如果测试失败,系统提示具体原因(如"连接超时""认证失败""Bucket 不存在"),小陈根据提示排查
    • 如果忘了填 bucket,保存时表单校验直接拦截

场景2:管理员清理过期文件

  • 用户:后台管理员小丽
  • 场景:季度末清理系统中过期的导入文件和临时附件
  • 操作步骤
    1. 进入"文件管理"页面
    2. 在搜索栏选择创建时间范围:"2026-01-01 至 2026-06-30"
    3. 在文件类型中输入"xlsx"筛选 Excel 文件
    4. 浏览列表,勾选确认不再需要的文件
    5. 点击"批量删除" → 弹窗确认"确定删除选中的 23 个文件吗?"
    6. 点击"确定" → 提示"删除成功"
  • 期望:文件记录删除的同时,存储介质中的实际文件也被清理,释放存储空间
  • 异常处理
    • 如果某个文件在存储介质中已经不存在了(手动删过),系统只删除数据库记录,不报错

场景3:前台用户上传头像

  • 用户:前台用户小林
  • 场景:小林想换一张个人头像
  • 操作步骤
    1. 进入"个人中心" → 点击头像区域
    2. 弹出文件选择框 → 选择一张 jpg 图片(2MB)
    3. 系统自动裁剪为正方形 → 预览确认
    4. 点击"确定上传" → 进度条一闪而过
    5. 头像更新成功
  • 期望:上传速度快(< 3 秒),头像立即可见
  • 背后发生了什么:前端调用 /app-api/infra/file/upload 接口 → 后端使用主配置(阿里云 OSS)存储 → 文件名自动改为基于内容哈希的名字(避免重名覆盖)→ 返回 OSS 上的访问 URL → 前端用这个 URL 展示新头像

场景4:前端直传大文件(预签名 URL 模式)

  • 用户:系统自动 / 前端应用
  • 场景:用户需要上传一个 200MB 的项目附件
  • 为什么不用普通上传:200MB 文件如果先传到后端、后端再转存到 OSS,服务器带宽会被占满,其他用户操作会变卡
  • 操作步骤
    1. 前端先调用 /admin-api/infra/file/presigned-url?name=bigfile.zip → 后端返回一个"预签名上传 URL"
    2. 前端拿到这个 URL,直接把文件 PUT 到 OSS(不经过后端服务器)
    3. 上传完成后,前端调用 /admin-api/infra/file/create 告诉后端"文件已经传好了,请记录一下"
    4. 后端在 infra_file 表中写入文件元信息(名称、大小、路径、URL)
  • 期望:200MB 文件上传不占用后端带宽,上传速度取决于用户到 OSS 的网速

场景5:开发者在业务模块中集成文件上传

  • 用户:后台开发者小韩
  • 场景:小韩在开发"商品管理"模块,需要给商品加图片上传功能
  • 操作步骤
    1. 在商品 Service 中注入 FileClient(文件客户端)
    2. 调用 fileClient.createFile(inputStream, fileName) → 返回文件 URL
    3. 把 URL 存到商品表的 imageUrl 字段
    4. 前端展示商品图片时,直接用这个 URL
  • 期望:不需要关心文件存在哪里(本地?OSS?),换存储方案时商品模块代码零改动
  • 为什么这样设计:所有存储细节(存在哪、怎么存、URL 怎么拼)都封装在 FileClient 里,业务代码只需要一行调用

场景6:从本地存储迁移到云存储

  • 用户:运维工程师小陈
  • 场景:系统最初用本地磁盘存储,现在要迁移到阿里云 OSS
  • 操作步骤
    1. 新建一个 OSS 存储配置 → 测试通过 → 设为主配置
    2. 此后所有新上传的文件自动存到 OSS
    3. 历史文件仍然可以通过原来的本地配置访问(因为每个文件记录都关联了它上传时用的配置 ID)
  • 期望:迁移过程平滑,历史文件不受影响,新文件自动走新通道

2.3 用户故事

编号用户故事优先级验收标准
US-01作为管理员,我希望能查看系统中所有已上传文件的列表,以便管理文件资源P0① 支持按文件路径模糊搜索 ② 支持按文件类型筛选 ③ 支持按创建时间范围筛选 ④ 默认按创建时间倒序 ⑤ 分页响应 < 500ms
US-02作为管理员,我希望能配置多种存储后端,以便灵活选择存储方案P0① 支持数据库/本地/FTP/SFTP/S3 五种类型 ② 选择类型后动态渲染对应的配置表单 ③ 保存后配置立即生效
US-03作为管理员,我希望能将某个存储配置设为主配置,以便控制默认的文件存储位置P0① 系统中同时只有一个主配置 ② 设置新主配置后旧主配置自动取消 ③ 新上传的文件使用主配置
US-04作为管理员,我希望能测试存储配置是否正确,以便上线前验证可用性P0① 点击测试后上传一个测试文件 ② 成功时返回测试文件 URL ③ 失败时返回具体错误原因 ④ 测试响应 < 5s
US-05作为管理员,我希望能删除不需要的文件,以便释放存储空间P0① 删除时同步删除存储介质中的实际文件 ② 删除前需二次确认 ③ 存储介质中不存在时仅删数据库记录
US-06作为管理员,我希望能批量删除文件,以便高效清理过期文件P1① 支持勾选多个文件批量删除 ② 批量删除时逐个处理,单个失败不影响其他
US-07作为用户,我希望能通过后端接口上传文件,以便快速上传小文件P0① 文件名自动生成(内容哈希),避免重名 ② 上传后返回文件访问 URL ③ < 5MB 文件上传响应 < 3s
US-08作为用户,我希望能通过预签名 URL 直传大文件,以便不占用后端带宽P0① 获取预签名 URL 响应 < 200ms ② 前端直传后需调用 create 接口记录元信息 ③ 直传文件可正常下载预览
US-09作为前台用户,我希望能上传头像,以便展示个人形象P0① 支持 jpg/png 格式 ② 上传后立即可见新头像 ③ 建议限制 5MB 以内
US-10作为前台用户,我希望能下载和预览文件,以便查看所需内容P0① 图片类型在浏览器中直接预览 ② 其他类型以附件形式下载 ③ 文件下载无需登录 ④ 首字节响应 < 500ms
US-11作为系统,我需要确保文件目录参数不包含非法路径,以防止目录穿越攻击P0① 目录参数不允许包含 .. ② 非法路径直接拒绝并返回错误

三、功能需求

3.1 后台管理端 - 文件管理

3.1.1 功能清单

功能优先级权限标识说明
文件列表(分页)P0infra:file:query分页展示文件,支持搜索筛选
文件详情P0infra:file:query查看单个文件详细信息
文件删除P0infra:file:delete删除单个文件(含存储介质)
批量删除文件P1infra:file:delete批量删除多个文件
文件预览/下载P0免登录通过配置 ID 和路径访问文件
后端上传文件P0登录即可通过后端接口上传单个文件
获取预签名 URLP0登录即可获取前端直传的预签名地址
创建文件记录P0登录即可前端直传后记录文件元信息

3.1.2 文件列表(分页)

页面描述:

文件管理列表

业务规则:

规则编号规则描述为什么这样设计
R-01默认按创建时间倒序排列最新上传的文件排在前面,方便管理员快速找到刚上传的文件
R-02支持按文件路径模糊搜索存储文件名是系统生成的哈希值,但管理员可能通过路径中的目录名来定位
R-03支持按文件类型(MIME)模糊搜索方便筛选特定类型的文件(如只找图片、只找 PDF)
R-04支持按创建时间范围筛选方便按时间段批量定位和清理文件
R-05列表不返回文件内容(content),仅返回元信息列表页只需要展示信息,加载文件内容会严重拖慢性能

数据字段:

字段名类型说明
idLong文件编号
configIdLong配置编号(关联存储配置)
nameString原文件名(用户上传时的文件名)
pathString文件路径(系统生成的存储文件名,含目录)
urlString文件访问地址(完整的 URL)
typeString文件 MIME 类型(如 image/png、application/pdf)
sizeLong文件大小(字节)
createTimeLocalDateTime创建时间

接口设计:

接口名称请求方式接口路径说明
获取文件分页GET/admin-api/infra/file/page获取文件分页列表

请求参数:

参数名类型必填说明
pathString文件路径(模糊匹配)
typeString文件类型(模糊匹配)
createTimeLocalDateTime[]创建时间范围
pageNoInteger页码
pageSizeInteger每页条数

返回结果: 标准分页结构(list + total)

3.1.3 文件删除

业务规则:

规则编号规则描述为什么这样设计
R-01删除数据库中的文件记录保持数据库记录与实际存储的一致性
R-02同时删除存储介质中的实际文件避免产生"孤儿文件"占用存储空间
R-03删除前需二次确认防止误操作,删除后文件不可恢复
R-04存储介质中文件不存在时,仅删除数据库记录,不报错可能已被手动清理,不应阻塞数据库记录清理
R-05批量删除时逐个处理,单个失败不影响其他避免因一个文件异常导致整批删除失败

接口设计:

接口名称请求方式接口路径说明
删除文件DELETE/admin-api/infra/file/delete删除单个文件
批量删除文件DELETE/admin-api/infra/file/delete-list批量删除文件

请求参数(删除):

参数名类型必填说明
idLong文件编号

请求参数(批量删除):

参数名类型必填说明
idsLong[]文件编号列表

3.1.4 文件预览/下载

页面描述:

  • 图片类型(jpg/png/gif/webp)→ 在浏览器中直接预览(Content-Type 返回对应的 image/* 类型)
  • 其他类型 → 以附件形式下载(Content-Disposition: attachment)
  • 该接口无需登录即可访问(PermitAll),且忽略租户隔离

为什么文件下载不需要登录:文件 URL 会嵌入在业务数据中(如商品图片、用户头像),如果每次加载图片都要登录态,前端展示会非常复杂且影响性能。文件 URL 本身包含的 configId + path 已经足够定位文件。

业务规则:

规则编号规则描述为什么这样设计
R-01URL 格式:/admin-api/infra/file/{configId}/get/{path}通过 configId 定位使用哪个存储配置,通过 path 定位具体文件
R-02无需登录即可访问文件 URL 需要能在各种场景下直接使用(邮件、分享、前端 img 标签)
R-03忽略租户隔离文件 URL 不区分租户,确保跨租户场景下也能正常访问
R-04文件不存在时返回 404标准的 HTTP 语义,前端和浏览器都能正确处理
R-05支持中文及特殊字符路径(自动 URL 解码)原文件名可能是中文,存储路径需要正确处理
R-06以附件形式返回,文件名取原文件名下载时使用用户能理解的文件名,而非哈希值

接口设计:

接口名称请求方式接口路径说明
下载文件GET/admin-api/infra/file/{configId}/get/**根据配置 ID 和路径下载文件

路径参数:

参数名类型必填说明
configIdLong存储配置编号
pathString文件路径(通配路径)

3.1.5 后端上传文件

页面描述:

  • 通过表单方式(multipart/form-data)上传文件
  • 可指定文件目录(directory),文件最终路径为 {directory}/{系统生成的文件名}
  • 上传成功后返回文件的访问 URL

两种上传模式对比:

上传模式对比

业务规则:

规则编号规则描述为什么这样设计
R-01使用当前主配置(master=true)的存储配置进行上传业务代码不需要指定存在哪,系统自动选择当前配置的存储方案
R-02文件名由系统自动生成(基于内容 SHA256 + 原始扩展名)① 避免文件名冲突(两个人上传同名文件不会覆盖)② 相同内容的文件只存一份(节省空间)
R-03文件目录格式需合法,不允许包含 .. 等非法路径防止目录穿越攻击(安全要求)
R-04上传完成后在 infra_file 表中记录文件元信息方便后续查询、管理、统计
R-05返回文件访问 URL前端拿到 URL 后可直接用于展示或存入业务表

接口设计(模式一:后端上传):

接口名称请求方式接口路径说明
上传文件POST/admin-api/infra/file/upload后端上传文件

请求参数(multipart/form-data):

参数名类型必填说明
fileMultipartFile文件附件
directoryString文件目录(如 product/images

返回结果:

字段名类型说明
dataString文件访问 URL

接口设计(模式二:前端直传 - 步骤1 获取预签名 URL):

接口名称请求方式接口路径说明
获取预签名地址GET/admin-api/infra/file/presigned-url获取前端直传的预签名上传 URL

请求参数:

参数名类型必填说明
nameString文件名称
directoryString文件目录

返回结果:

字段名类型说明
configIdLong配置编号
uploadUrlString预签名上传 URL(前端用这个 URL 直传文件到存储)
urlString文件访问 URL(上传完成后可通过此 URL 访问)
pathString文件路径(直传后需用此 path 调用 create 接口)

接口设计(模式二:前端直传 - 步骤2 记录文件元信息):

接口名称请求方式接口路径说明
创建文件记录POST/admin-api/infra/file/create前端直传完成后记录文件元信息

请求参数:

参数名类型必填说明
configIdLong文件配置编号(取自预签名 URL 返回的 configId)
pathString文件路径(取自预签名 URL 返回的 path)
nameString原文件名
urlString文件访问 URL
typeString文件 MIME 类型
sizeLong文件大小(字节)

返回结果:

字段名类型说明
dataLong新创建的文件记录 ID

3.2 后台管理端 - 存储配置管理

3.2.1 功能清单

功能优先级权限标识说明
配置列表(分页)P0infra:file-config:query分页展示存储配置
配置详情P0infra:file-config:query查看单个配置详细信息
创建配置P0infra:file-config:create新增存储后端配置
更新配置P0infra:file-config:update修改存储配置
删除配置P0infra:file-config:delete删除存储配置
批量删除配置P1infra:file-config:delete批量删除多个配置
设置主配置P0infra:file-config:update将某个配置设为默认
配置测试P0infra:file-config:query测试配置是否可用

3.2.2 配置列表(分页)

页面描述:

存储配置列表

业务规则:

规则编号规则描述为什么这样设计
R-01默认按创建时间倒序排列最新创建的配置排在前面
R-02支持按配置名模糊搜索配置多了之后方便快速定位
R-03支持按存储器类型筛选方便查看某一类存储的配置
R-04主配置通过特殊标识(⭐)高亮显示让管理员一眼看出当前文件存在哪里

数据字段:

字段名类型说明
idLong配置编号
nameString配置名(如"阿里云 OSS - 生产环境")
storageInteger存储器类型(见枚举表)
masterBoolean是否为主配置
configObject存储配置详情(不同存储器结构不同)
remarkString备注
createTimeLocalDateTime创建时间

存储器类型枚举:

枚举值存储器类型适用场景成本
1数据库(DB)极小文件(头像图标),不想额外部署存储服务高(占用数据库空间)
10本地磁盘(Local)开发环境、单机部署低(但不好扩展)
11FTP已有 FTP 服务器的传统企业
12SFTP需要安全传输的企业
20S3 兼容对象存储生产环境推荐,支持阿里云 OSS / 腾讯云 COS / MinIO 等按需付费,推荐

3.2.3 创建存储配置

页面描述:

新增存储配置

各存储器配置参数:

数据库(storage=1):

配置项类型必填说明
domainString(URL)自定义域名,用于生成文件访问 URL

本地磁盘(storage=10):

配置项类型必填说明
basePathString本地存储基础路径(如 /data/uploads
domainString(URL)自定义域名,用于生成文件访问 URL

FTP(storage=11):

配置项类型必填说明
basePathStringFTP 服务器上的基础路径
domainString(URL)自定义域名
hostStringFTP 主机地址
portIntegerFTP 端口号
usernameStringFTP 用户名
passwordStringFTP 密码
modeString连接模式(Active/Passive)

SFTP(storage=12):

配置项类型必填说明
basePathStringSFTP 服务器上的基础路径
domainString(URL)自定义域名
hostStringSFTP 主机地址
portIntegerSFTP 端口号
usernameStringSFTP 用户名
passwordStringSFTP 密码

S3 兼容对象存储(storage=20):

配置项类型必填说明
endpointString节点地址(如 oss-cn-hangzhou.aliyuncs.com
domainString(URL)自定义域名(七牛云必填)
bucketString存储 Bucket 名称
accessKeyString访问 Key
accessSecretString访问 Secret
enablePathStyleAccessBoolean是否启用 PathStyle(MinIO 通常为 true)
enablePublicAccessBoolean是否公开访问
regionString区域(AWS S3 必填)

S3 兼容云厂商对照表:

云厂商endpoint 示例domain 说明region 说明
阿里云 OSSoss-cn-hangzhou.aliyuncs.com绑定自定义域名自动识别
腾讯云 COScos.ap-guangzhou.myqcloud.com绑定自定义域名自动识别
七牛云s3.cn-south-1.qiniucs.com必须配置 domain自动识别
MinIOhttp://127.0.0.1:9000通过 Nginx 配置不需要
华为云 OBSobs.cn-north-4.myhuaweicloud.com绑定自定义域名自动识别
火山云 TOStos-s3-cn-beijing.volces.com绑定自定义域名自动识别
AWS S3s3.us-east-1.amazonaws.com绑定自定义域名必须填写

为什么支持这么多云厂商:S3 是对象存储的事实标准协议,几乎所有云厂商的对象存储都兼容 S3 API。PMForge 统一使用 S3 协议对接,所以一套代码就能支持所有主流云存储。

接口设计:

接口名称请求方式接口路径说明
创建配置POST/admin-api/infra/file-config/create创建存储配置

请求参数:

参数名类型必填说明
nameString配置名
storageInteger存储器类型
configMap<String, Object>存储配置参数(不同存储器结构不同)
remarkString备注

返回结果:

字段名类型说明
dataLong新创建的配置 ID

3.2.4 更新存储配置

接口设计:

接口名称请求方式接口路径说明
更新配置PUT/admin-api/infra/file-config/update更新存储配置

请求参数:

参数名类型必填说明
idLong配置编号
nameString配置名
storageInteger存储器类型
configMap<String, Object>存储配置参数
remarkString备注

3.2.5 删除存储配置

业务规则:

规则编号规则描述为什么这样设计
R-01删除前需二次确认删除配置后,该配置下的历史文件将无法下载
R-02主配置不允许删除必须有一个默认存储方案,需先取消主配置身份
R-03已被文件引用的配置删除后,相关历史文件将无法下载文件记录中的 configId 指向已删除的配置,读取时会报错

接口设计:

接口名称请求方式接口路径说明
删除配置DELETE/admin-api/infra/file-config/delete删除单个存储配置
批量删除配置DELETE/admin-api/infra/file-config/delete-list批量删除存储配置

请求参数(删除):

参数名类型必填说明
idLong配置编号

请求参数(批量删除):

参数名类型必填说明
idsLong[]配置编号列表

3.2.6 设置主配置

业务规则:

规则编号规则描述为什么这样设计
R-01系统中同一时刻只能有一个主配置上传文件时需要明确知道存在哪里,不能有歧义
R-02设置新的主配置后,原主配置自动取消保证唯一性,类似"默认地址"的概念
R-03文件上传默认使用主配置业务代码不需要指定存储位置

接口设计:

接口名称请求方式接口路径说明
设为主配置PUT/admin-api/infra/file-config/update-master将指定配置设为默认主配置

请求参数:

参数名类型必填说明
idLong配置编号

3.2.7 配置测试

页面描述:

  • 点击"测试"按钮后,系统使用该配置上传一个测试文件
  • 测试成功 → 返回测试文件的访问 URL(可以点击验证)
  • 测试失败 → 返回具体的错误原因

业务规则:

规则编号规则描述为什么这样设计
R-01上传一个测试文件到目标存储只有真正上传成功才能证明配置正确
R-02测试成功返回测试文件的访问 URL管理员可以点击 URL 验证文件是否真的可访问
R-03测试失败返回具体的错误原因方便管理员排查(连接失败?认证失败?Bucket 不存在?)

接口设计:

接口名称请求方式接口路径说明
测试配置GET/admin-api/infra/file-config/test测试存储配置是否可用

请求参数:

参数名类型必填说明
idLong配置编号

返回结果:

字段名类型说明
dataString测试文件的访问 URL(成功时返回)

3.3 前台用户端

3.3.1 功能清单

功能优先级说明
上传文件(后端模式)P0通过后端接口上传文件(头像、图片等)
获取预签名 URLP0获取前端直传的预签名地址
创建文件记录P0前端直传完成后记录文件元信息

3.3.2 上传文件

业务规则:

规则编号规则描述为什么这样设计
R-01使用当前主配置的存储配置进行上传前台用户不需要关心文件存在哪里
R-02文件名由系统自动生成避免文件名冲突
R-03可指定文件目录(如 avatarimages不同类型的文件分目录存储,方便管理
R-04文件目录不允许包含 .. 等非法路径防止目录穿越攻击
R-05头像上传建议限制格式为 jpg/png,大小不超过 5MB头像不需要太大,限制大小节省存储空间
R-06前台上传接口当前为 PermitAll(免登录)生产环境需评估:如果只允许登录用户上传,应改为"登录即可"

接口设计:

接口名称请求方式接口路径说明
上传文件POST/app-api/infra/file/upload前台上传文件
获取预签名地址GET/app-api/infra/file/presigned-url获取前端直传预签名 URL
创建文件记录POST/app-api/infra/file/create前端直传后记录文件元信息

请求参数(上传文件,multipart/form-data):

参数名类型必填说明
fileMultipartFile文件附件
directoryString文件目录(如 avatar

返回结果(上传文件):

字段名类型说明
dataString文件访问 URL

请求参数(获取预签名地址):

参数名类型必填说明
nameString文件名称
directoryString文件目录

返回结果(获取预签名地址):

字段名类型说明
configIdLong配置编号
uploadUrlString预签名上传 URL
urlString文件访问 URL
pathString文件路径

请求参数(创建文件记录):

参数名类型必填说明
configIdLong文件配置编号
pathString文件路径
nameString原文件名
urlString文件访问 URL
typeString文件 MIME 类型
sizeLong文件大小(字节)

返回结果(创建文件记录):

字段名类型说明
dataLong新创建的文件记录 ID

3.3.3 文件下载/预览

前台用户的文件下载/预览复用后台管理端的下载接口(/admin-api/infra/file/{configId}/get/**),该接口无需鉴权且忽略租户隔离。


四、非功能需求

4.1 性能要求

指标目标值说明
文件分页查询< 500ms文件表数据量大时需关注索引优化
文件详情查询< 200ms单条记录查询
小文件上传(< 5MB)< 3s包含网络传输时间
预签名 URL 获取< 200ms仅生成 URL,不涉及文件传输
文件下载首字节< 500ms用户感知"开始下载"的速度
配置测试< 5s包含上传测试文件到远端存储的时间

4.2 安全要求

安全项实现方式说明
目录穿越防护文件目录参数校验,不允许包含 ..防止攻击者通过路径参数访问系统敏感文件
敏感信息保护accessKey、accessSecret、password 等加密存储防止数据库泄露导致存储凭证泄露
权限控制后台管理端接口需对应权限标识防止未授权用户管理文件和配置
文件类型校验上传时校验 MIME 类型防止上传可执行文件等危险文件
前台接口鉴权评估生产环境评估是否需要登录避免匿名用户滥用上传功能

4.3 兼容性要求

要求
PC 浏览器Chrome 80+、Firefox 75+、Safari 13+
移动端iOS Safari 12+、Android Chrome 80+
文件上传支持主流浏览器的 File API 和 FormData

4.4 可扩展性要求

要求说明
存储后端扩展新增存储后端只需实现 FileClient 接口和对应的 Config 类,并注册到 FileStorageEnum 枚举
配置参数动态化存储配置参数使用 Map 动态接收,前端根据存储器类型渲染对应表单
未来可扩展方向① 文件分片上传(超大文件)② 文件 CDN 加速集成 ③ 文件版本管理 ④ 存储空间配额管理

五、数据设计

5.1 数据模型

文件表(infra_file)

字段名类型必填说明
idBIGINT文件编号(主键,自增)
config_idBIGINT配置编号(关联 infra_file_config.id)
nameVARCHAR(256)原文件名
pathVARCHAR(512)文件路径(系统生成的存储文件名)
urlVARCHAR(512)文件访问地址
typeVARCHAR(128)文件 MIME 类型
sizeINT文件大小(字节)
creatorVARCHAR(64)创建者
create_timeDATETIME创建时间
updaterVARCHAR(64)更新者
update_timeDATETIME更新时间
deletedBIT(1)删除标记(0-未删除 1-已删除)
tenant_idBIGINT租户编号(该表忽略租户隔离)

租户隔离说明:该表标注了 @TenantIgnore,即忽略租户隔离,所有租户共享文件记录。这是因为文件 URL 需要跨租户可访问(如系统级资源)。

索引设计:

索引名字段类型说明
idx_config_idconfig_id普通索引按配置查询文件
idx_create_timecreate_time普通索引按时间范围筛选文件

文件配置表(infra_file_config)

字段名类型必填说明
idBIGINT配置编号(主键,自增)
nameVARCHAR(64)配置名
storageINT存储器类型(1-DB 10-Local 11-FTP 12-SFTP 20-S3)
masterBIT(1)是否为主配置(0-否 1-是)
configVARCHAR(4096)存储配置(JSON 格式)
remarkVARCHAR(256)备注
creatorVARCHAR(64)创建者
create_timeDATETIME创建时间
updaterVARCHAR(64)更新者
update_timeDATETIME更新时间
deletedBIT(1)删除标记
tenant_idBIGINT租户编号(该表忽略租户隔离)

config 字段说明:JSON 格式,不同存储器的配置参数结构不同。例如 S3 类型存储 endpoint、bucket、accessKey 等;本地类型存储 basePath、domain 等。

索引设计:

索引名字段类型说明
uk_mastermaster唯一索引(partial)保证只有一个主配置(master=1 只有一条)

文件内容表(infra_file_content)

字段名类型必填说明
idBIGINT编号(主键,自增)
config_idBIGINT配置编号
pathVARCHAR(512)文件路径(与 infra_file.path 对应)
contentLONGBLOB文件二进制内容
creatorVARCHAR(64)创建者
create_timeDATETIME创建时间
updaterVARCHAR(64)更新者
update_timeDATETIME更新时间
deletedBIT(1)删除标记
tenant_idBIGINT租户编号(该表忽略租户隔离)

仅用于数据库存储模式:当 storage=1(数据库存储)时,文件的二进制内容存储在此表的 content 字段中。其他存储模式(本地/FTP/S3 等)不使用此表。

5.2 数据关系

文件存储数据关系

5.3 数据字典

字典类型字典值说明
infra_file_storage1-数据库、10-本地磁盘、11-FTP、12-SFTP、20-S3 对象存储存储器类型
infra_file_ftp_modeActive-主动模式、Passive-被动模式FTP 连接模式

六、跨模块联动

6.1 联动关系总览

联动模块联动方式数据流向说明
用户管理头像 URL 存储用户模块 → 文件存储用户上传头像后,文件 URL 存入用户表的 avatar 字段
商品管理(商城模块)商品图片存储商品模块 → 文件存储商品图片上传后,URL 存入商品表的 image 字段
工作流(附件)流程附件存储工作流模块 → 文件存储审批流程中的附件通过文件存储上传
数据字典存储器类型字典字典模块 → 文件存储提供存储器类型的字典数据
日志审计操作日志记录文件存储 → 日志模块文件上传/删除等操作记录到操作日志
租户管理租户隔离租户模块 → 文件存储文件存储忽略租户隔离(@TenantIgnore),但业务层按需控制
配置管理系统参数配置模块 → 文件存储可通过参数配置上传文件大小限制等

6.2 关键联动流程

用户头像上传全流程

用户头像上传全流程

工作流附件上传流程

工作流附件上传流程

6.3 数据一致性要求

场景一致性要求处理方式
文件删除数据库记录与存储介质同步删除先删存储介质,再删数据库记录;介质不存在时仅删数据库
配置删除历史文件可访问性配置删除后,历史文件的 configId 失效,无法下载 → 删除前需二次确认
主配置切换新旧文件共存新文件用新主配置,旧文件仍用原配置 → 每个文件记录了自己的 configId

七、附录

7.1 名词解释

术语通俗解释
存储配置告诉系统"文件存在哪里"的一份配置信息,就像快递系统的"仓库地址"
主配置(Master)默认的存储配置。上传文件时如果不特别指定,就存在主配置指定的地方。类似手机的"默认 SIM 卡"
预签名 URL一个"临时通行证"——后端生成一个带时效的 URL,前端拿着这个 URL 可以直接把文件传到云存储,不需要经过后端服务器。就像快递柜的取件码,不用去柜台就能存/取包裹
S3 兼容存储S3 是亚马逊发明的对象存储协议,现在几乎所有云存储(阿里云 OSS、腾讯云 COS、MinIO 等)都支持这个协议。PMForge 用 S3 协议统一对接所有云存储
MIME 类型文件的"身份证类型",告诉浏览器这个文件是什么格式——image/png 是图片,application/pdf 是 PDF 文档
PathStyleS3 存储的一种 URL 格式:http://服务器地址/Bucket名/文件路径。MinIO 自建存储通常需要开启这个模式
目录穿越攻击一种安全攻击方式——攻击者在文件路径中插入 ..(返回上级目录),试图访问系统敏感文件。比如 ../../etc/passwd 就能读到系统密码文件。PMForge 会拦截路径中的 .. 来防止这种攻击
DB 存储模式把文件二进制内容直接存在数据库里。优点是简单(不需要额外存储服务),缺点是数据库会变大变慢。适合存头像等极小文件
SHA256 哈希命名用文件内容的哈希值作为文件名。好处:① 同名文件不会覆盖 ② 相同内容的文件只存一份(去重)
Bucket对象存储中的"容器"概念,类似文件夹。一个 Bucket 可以装无数文件,每个文件有自己的路径
FileClientPMForge 内部的文件操作接口。业务代码通过这个接口上传/下载文件,不需要关心底层用的是哪种存储。就像用"快递 API"下单,不需要知道快递公司用的什么运输方式
前端直传文件直接从浏览器传到云存储,不经过后端服务器。适合大文件场景,避免服务器带宽成为瓶颈

7.2 接口汇总

后台管理端 - 文件管理

接口名称请求方式接口路径权限标识说明
上传文件POST/admin-api/infra/file/upload登录即可后端上传文件
获取预签名地址GET/admin-api/infra/file/presigned-url登录即可获取前端直传预签名 URL
创建文件记录POST/admin-api/infra/file/create登录即可前端直传后记录文件元信息
获取文件详情GET/admin-api/infra/file/getinfra:file:query获取文件详细信息
获取文件分页GET/admin-api/infra/file/pageinfra:file:query获取文件分页列表
删除文件DELETE/admin-api/infra/file/deleteinfra:file:delete删除单个文件
批量删除文件DELETE/admin-api/infra/file/delete-listinfra:file:delete批量删除文件
下载文件GET/admin-api/infra/file/{configId}/get/**免登录下载/预览文件

后台管理端 - 存储配置

接口名称请求方式接口路径权限标识说明
创建配置POST/admin-api/infra/file-config/createinfra:file-config:create创建存储配置
更新配置PUT/admin-api/infra/file-config/updateinfra:file-config:update更新存储配置
删除配置DELETE/admin-api/infra/file-config/deleteinfra:file-config:delete删除单个配置
批量删除配置DELETE/admin-api/infra/file-config/delete-listinfra:file-config:delete批量删除配置
获取配置详情GET/admin-api/infra/file-config/getinfra:file-config:query获取配置详细信息
获取配置分页GET/admin-api/infra/file-config/pageinfra:file-config:query获取配置分页列表
设为主配置PUT/admin-api/infra/file-config/update-masterinfra:file-config:update设置默认主配置
测试配置GET/admin-api/infra/file-config/testinfra:file-config:query测试配置可用性

前台用户端

接口名称请求方式接口路径权限标识说明
上传文件POST/app-api/infra/file/upload免登录前台上传文件
获取预签名地址GET/app-api/infra/file/presigned-url登录即可获取前端直传预签名 URL
创建文件记录POST/app-api/infra/file/create免登录前端直传后记录文件元信息
下载文件GET/admin-api/infra/file/{configId}/get/**免登录下载/预览文件(复用后台接口)

7.3 权限配置建议

权限标识说明推荐角色
infra:file:query文件查询后台管理员、系统管理员
infra:file:delete文件删除系统管理员
infra:file-config:query存储配置查询系统管理员、运维工程师
infra:file-config:create存储配置创建系统管理员
infra:file-config:update存储配置更新系统管理员
infra:file-config:delete存储配置删除系统管理员

7.4 注意事项

事项说明
前台上传接口鉴权当前前台上传接口配置为 PermitAll(免登录),生产环境应根据业务需要评估是否增加登录鉴权
文件删除策略当前文件删除为物理删除(同时删除存储介质中的文件和数据库记录),如需保留历史可改为逻辑删除
大文件上传对于 > 100MB 的文件,强烈建议使用前端直传(预签名 URL 模式),避免后端服务器带宽瓶颈
存储配置变更修改存储配置后,已上传的历史文件仍可通过原配置访问,新上传的文件使用新的主配置
数据库存储限制使用数据库存储模式时,文件内容存储在 LONGBLOB 字段中,不建议存储大文件(建议 < 1MB)

7.5 变更记录

版本日期修改内容修改人
v1.02026-09-17初始版本PM Team
v2.02026-09-19全面增强:补充业务场景与人物画像、增加验收标准、新增 ASCII 页面原型、补充跨模块联动、新增名词解释、增加设计 rationalePM Team

本文档为文件存储模块 PRD v2.0,如有问题请联系产品负责人。