外观
文件与存储
概述
文件与存储模块为系统提供统一的文件上传、下载、管理与多存储后端能力。它把"存哪里"和"怎么存"解耦:业务侧只关心文件本身,具体落地到本地磁盘还是 S3 对象存储,由后端的 文件配置 决定。
模块包含两块:
- 文件配置:维护一组存储客户端配置(本地磁盘 / S3 对象存储),其中一条标记为"主配置",是所有落地上传的默认目标。
- 文件列表:记录已上传文件的结构化信息(名称、路径、大小、类型、所属配置),并提供上传、下载、删除、预签名等操作。
设计上采用 策略模式 + 工厂模式,新增一种存储类型只需在 FileClientFactory 注册一个客户端实现,业务代码无需改动。
核心概念
存储配置(infra_file_config)
每一条配置描述"如何连接并写入某个存储后端"。核心字段为 storage(存储器类型,见下表)与 config(存储相关的具体参数,以 JSON 字典保存)。
| 存储器 | 枚举值 | 说明 |
|---|---|---|
| 本地磁盘 | 10 | 写入服务器本地目录,依赖静态资源服务对外提供访问 |
| S3 对象存储 | 20 | 兼容 MinIO、阿里云 OSS、腾讯云 COS、七牛云、华为云 OBS 等 S3 协议服务 |
主配置(master)
系统中有且仅有一条主配置。所有"落库"上传(文件列表中的上传、头像、参数附件等)都先读取主配置,再由其对应的存储客户端完成写入。删除主配置会被拒绝。
文件记录(infra_file)
记录一次上传的元数据。注意:文件的 url 不落库,而是在查询时根据所属配置和存储策略实时计算,因此更换存储或域名后无需迁移数据即可生效。
架构设计
文件存储能力下沉到 app/framework/file 框架层,业务模块 module_infra 只负责记录与编排,二者通过统一的 FileClient 接口交互:
FileClientFactory 以配置编号为维度缓存已创建的客户端;配置更新时会重建客户端,保证热更新生效。
存储类型
本地存储(LOCAL)
文件写入 STATIC_ROOT 下的子目录(默认 upload)。访问地址由 ROOT_PATH + STATIC_URL + 基础路径 + 文件相对路径 拼接而成,并挂载为静态资源对外提供。
基础路径(
basePath)必须落在STATIC_ROOT之内,否则视为非法配置,避免文件写到静态目录之外无法访问。
S3 存储(S3)
基于 aiobotocore 异步实现。上传、读取、删除都直接操作对象存储;额外支持 预签名上传 / 读取地址,便于前端直连对象存储、减轻后端流量压力。
- 未配置自定义域名时,会自动按
endpoint + bucket(MinIO 风格)或bucket + endpoint(云厂商风格)推断访问域名。 - 公开访问开启时,读取直接返回公开 URL;私有访问则生成带有效期的 GET 预签名地址。
文件配置管理

在「基础设施 → 文件配置」中维护存储客户端。一条配置的关键信息:
- 配置名:便于识别的名称。
- 存储器:本地磁盘(
10)或 S3 对象存储(20),创建后不可修改。 - 主配置:标记当前配置是否为主配置,全局唯一。
- 备注:可选说明。
- config:随存储器类型变化的参数集合。
不同存储器所需的 config 参数:
| 参数 | 本地 | S3 | 说明 |
|---|---|---|---|
basePath | ✔ | 本地存储根目录下的子目录,默认 upload | |
endpoint | ✔ | 节点地址 | |
bucket | ✔ | 存储桶 | |
accessKey / accessSecret | ✔ | 访问密钥 | |
enablePathStyleAccess | ✔ | 是否启用 Path Style 访问(MinIO 等需要) | |
enablePublicAccess | ✔ | 是否公开访问 | |
region | △ | 区域,一般仅 AWS 需要 | |
domain | ✔ | ✔ | 自定义访问域名(主机地址) |
配置保存后,可点击 测试 按钮:后端会真实写入一个测试文件并读回校验,确保连接与权限配置正确。
文件管理

文件列表页面(「基础设施 → 文件列表」)展示所有上传记录,支持分页查询、上传、复制访问链接、下载/预览、删除与批量删除。图片类型直接缩略图预览,PDF 可在线预览。
上传流程
系统支持两种上传模式,由前端环境变量 VITE_UPLOAD_TYPE 控制:
- 后端直传(server):文件先发到后端
/infra/file/upload,由后端写入主配置对应的存储。 - 前端直连(client,仅 S3):前端先向后端申请预签名地址,再直接
PUT到对象存储,最后将文件信息异步回写后端。
落库上传的路径规则为:业务目录 / 日期目录(YYYYMMDD) / UUID.扩展名,并带有路径穿越清洗,保证文件名与目录安全。
公开下载
GET /infra/file/{configId}/get/{path:path} 是经鉴权的文件内容读取接口,可直接用于 <img src>、附件下载等场景,由后端按配置编号定位存储客户端再取回二进制内容。
安全与校验
上传链路在多处统一做了安全校验(集中在 FileUploadUtil),与具体存储策略无关:
- 大小限制:超过
MAX_FILE_SIZE(默认 50MB)拒绝上传。 - 危险扩展名黑名单:
DANGEROUS_EXTENSIONS中的类型(如.py、.sh、.exe等可执行/脚本类)拒绝上传。 - 路径穿越防护:文件名、业务目录、下载路径均会清洗
../、..\\、\0等危险片段;本地客户端在路径解析层还会二次校验,确保文件不会跳出存储根目录。 - 内容类型校验:通过文件魔数检测真实类型,与声明扩展名不一致时告警,降低伪装文件风险。
相关配置
| 配置项 | 位置 | 默认值 | 说明 |
|---|---|---|---|
STATIC_ROOT | 后端 settings | static/ | 本地存储根目录 |
STATIC_URL | 后端 settings | /static | 静态资源访问前缀 |
UPLOAD_DIR_NAME | 后端 settings | upload | 本地存储默认基础路径 |
MAX_FILE_SIZE | 后端 settings | 50 * 1024 * 1024 | 单文件最大字节数(50MB) |
DANGEROUS_EXTENSIONS | 后端 settings | 脚本/可执行黑名单 | 禁止上传的扩展名 |
VITE_UPLOAD_TYPE | 前端 .env | — | client 前端直连 / server 后端直传 |
关键接口
| 接口 | 方法 | 说明 |
|---|---|---|
/infra/file/page | GET | 分页查询文件列表 |
/infra/file/upload | POST | 上传文件并落库 |
/infra/file/presigned-url | GET | 获取预签名上传地址 |
/infra/file/download | POST | 下载文件 |
/infra/file/{configId}/get/{path} | GET | 公开读取文件内容 |
/infra/file/delete-list | DELETE | 批量删除文件(含物理文件) |
/infra/file-config/page | GET | 分页查询文件配置 |
/infra/file-config/create / /update | POST / PUT | 新增 / 修改配置 |
/infra/file-config/update-master | PUT | 设置主配置 |
/infra/file-config/test | GET | 测试存储连通性 |
前端实现
文件相关页面与组件位于 apps/web-ele:
- 文件配置页:
views/infra/fileConfig,表单modules/form.vue+data.ts(根据storage动态展示本地 / S3 字段),API 封装在api/infra/file-config。 - 文件列表页:
views/infra/file,支持缩略图预览、复制链接、下载/预览、删除,API 封装在api/infra/file。 - 通用上传能力:
components/upload/use-upload.ts提供useUpload(directory)钩子,封装"前端直连 / 后端直传"两种模式与预签名逻辑;并基于它封装了image-upload、file-upload、input-upload等可复用组件,供头像、知识库、富文本等场景直接使用。