Storage Starter
受众:开发者 摘要:统一文件存储 Facade。
public/local/memory+ S3 / 阿里云 OSS / 腾讯云 COS / MinIO / 七牛 / 华为 / 百度 / 又拍 / 火山 / Azure / GCS / FTP / SFTP / WebDAV。临时签名 URL + 缩略图 + 内容 URL 重写 + 媒体资产描述。
何时使用
| 场景 | 建议 |
|---|---|
| 公开文件(图片、CSS、JS) | public disk |
| 私有文件(合同、密钥、证书) | local disk + 临时签名 URL |
| 单元测试 / 预览 | memory provider |
| AWS S3 / R2 / 通用 S3 兼容 | s3 provider |
| 国内云对象存储 | aliyun-oss / tencent-cos / qiniu-kodo / huawei-obs / minio / baidu-bos / upyun-uss / volcengine-tos |
| 海外云对象存储 | azure-blob / google-cloud-storage |
| 传统文件服务器 | ftp / sftp / webdav |
| 用户头像 / 内容图片 / 售后附件 | /api/v1/file/users/me/records 当前用户文件记录接口 |
| 后台文件管理 | Core File 的 /api/v1/file/records + file_record 元数据 |
| 域名 / CDN 切换 | url-aliases + Storage.rewriteHtml(...) |
业务代码只调 Storage Facade / StorageManager / StorageProvider,不直接调 x-file-storage API。
快速开始
StorageWriteResult result = Storage.put("avatars/1.png", bytes);
String url = Storage.url("avatars/1.png");
byte[] content = Storage.read("avatars/1.png");
Storage.delete("avatars/1.png");指定 disk:
Storage.put("s3", StorageFile.of("reports/a.txt", bytes));
Storage.put("tencent-cos", StorageFile.of("reports/a.txt", bytes));
Storage.read("memory", "reports/a.txt");Facade API
接口设计参考 Laravel Filesystem 常用开发体验。
| 方法 | 说明 |
|---|---|
put(path, bytes) | 写字节 |
putString(path, content) | 写 UTF-8 文本 |
get(path) | 读 StorageFile |
read(path) / readString(path) | 读字节 / 文本 |
exists(path) / missing(path) | 存在判断 |
size(path) / mimeType(path) / lastModified(path) | 元信息 |
copy(sourcePath, targetPath) | 同 disk 复制 |
copy(srcDisk, srcPath, tgtDisk, tgtPath) | 跨 disk 复制 |
move(...) | 移动(同上重载) |
files(directory) / directories(directory) | 列表 |
delete(path) / deleteDirectory(directory) | 删除 |
url(path) | 生成访问地址 |
thumbnailUrl(path) | 缩略图 URL(不支持返回空) |
temporaryUrl(disk, path, ttl) | 临时签名 URL |
temporaryUrlInfo(disk, path, ttl) | 完整签名信息 |
visibility(disk) / isPublic(disk) / isPrivate(disk) | disk 可见性 |
provider(name) | 获取指定 disk 对应的 StorageProvider |
rewriteHtml(html) / rewriteUrl(url) / rewriteText(text) | URL 重写 |
媒体资产描述
业务模块如果已经沉淀了图片、封面或附件 URL 字段,可以用 StorageMediaAssets 把旧字段投影成中性的 StorageMediaAsset 列表,再由 VO 返回 mediaAssets:
List<StorageMediaAsset> assets = StorageMediaAssets.ofUrl(
"cms", "banner", "imageUrl", content.getImageUrl());如果业务侧已经有缩略图、封面或视频基础 metadata,可以通过 StorageMediaAssetOptions 一起投影,避免各模块重复定义字段口径:
StorageMediaAsset video = StorageMediaAssets.fromUrl(
"mall", "product", "videoUrl", product.getVideoUrl(), 0,
StorageMediaAssetOptions.video(product.getVideoCoverUrl(), 1920, 1080, 123_000L, "video/mp4"));StorageMediaAsset 字段口径:
| 字段 | 说明 |
|---|---|
consumer | 消费方,如 cms、blog、mall |
businessType | 业务类型,如 banner、article、product |
field | 来源字段,如 imageUrl、coverUrl、imageUrls |
assetType | image / video / audio / document / file |
url | 原始访问 URL |
thumbnailUrl / coverUrl | 图片类资产默认复用原始 URL;非图片为空,后续可由缩略图或视频封面链路填充 |
contentSafetyScene | 按 consumer.businessType.field 生成的内容安全隔离场景 |
sortOrder | 多 URL 字段拆分后的排序 |
metadata | 轻量元信息,默认包含 URL 扩展名;视频可附带 posterUrl、width、height、durationMillis、contentType 等 |
这只是读侧 contract,不替代文件元数据或素材库。保存长期身份时仍优先使用 disk + path 或 file_record 元数据 ID;业务写侧已经生成的缩略图、视频封面和基础 metadata 可以先通过 StorageMediaAssetOptions 暴露给三端。Core File 文件元数据已返回 mediaAssets,Admin 文件记录页可展示素材预览和内容安全 scene,并支持按素材类型、生命周期、业务归属筛选;该页也可触发图片文件缩略图生成 / 回填,以及图片文件内容安全批量检测 / 回填。后台 /api/v1/file/records/bind 与 /api/v1/file/records/unbind 已提供文件记录业务绑定 / 解绑写侧 contract;Admin 上传体验页已接入当前用户业务文件绑定、查询和解绑,素材库独立选择器继续按后续功能簇推进。
指定 disk 同名重载:
Storage.putString("tencent-cos", "docs/readme.txt", "hello");
Storage.copy("tencent-cos", "docs/readme.txt", "archive/readme.txt");
Storage.files("tencent-cos", "docs");跨 disk 复制 / 移动:
Storage.copy("public", "uploads/a.png", "tencent-cos", "images/a.png");
Storage.move("public", "uploads/b.png", "aliyun-oss", "images/b.png");- 同 disk 优先走底层原生能力;不可用时回退
read + put或copy + delete - 跨 disk 由框架统一
source get → target put;移动 = 复制 + 删除源 - 对象存储「目录」通常是逻辑前缀
public/local都用local-plus,具备分片、列表、复制 / 移动、缩略图
配置项
spring:
open:
storage:
enabled: true
default-provider: public
register-common-providers: true
url: "" # 服务商 provider 全局公开 URL 前缀
thumbnail-suffix: ".min.jpg"
temporary-url:
enabled: true
route: /api/v1/file/records/download
ttl: 10m
secret: ${SPRING_OPEN_STORAGE_TEMPORARY_URL_SECRET:}
url-rewrite:
enabled: true
attributes: src,href,poster,data-src,data-original
srcset-attributes: srcset,data-srcset
rewrite-style-urls: true
config:
enabled: true
prefix: spring.open.storage
providers:
public:
enabled: true
provider: local-plus
platform: public
visibility: public
root: ${APP_PUBLIC_DIR:${user.dir}/public}
url: /
local:
enabled: true
provider: local-plus
platform: local
visibility: private
root: ${APP_STORAGE_DIR:${user.dir}/storage}/app/private
memory:
enabled: true
capacity: 1000通用参数
| 配置 | 默认值 | 说明 |
|---|---|---|
enabled | true | 启用开关 |
default-provider | public | 默认 provider 配置名;Storage API 中仍可把该名称作为 disk 参数使用 |
register-common-providers | true | 自动注册常见 provider |
thumbnail-suffix | .min.jpg | 缩略图后缀 |
temporary-url.enabled | true | 临时签名 URL |
temporary-url.route | /api/v1/file/records/download | 临时签名下载路由 |
temporary-url.ttl | 10m | 默认 TTL |
temporary-url.secret | 空 | 生产必须配置 |
url-rewrite.enabled | true | 内容 URL 重写 |
config.enabled | true | 数据库覆盖 |
Provider 公共字段
| 字段 | 默认 | 说明 |
|---|---|---|
enabled | true | 启停 |
provider | <provider> | 真实 provider |
platform | <provider> | 底层平台标识 |
visibility | public | private 时不生成公开 URL |
url | 空 | 公开 URL 前缀 / CDN 域名 |
url-aliases | 空 | 历史 URL 前缀,URL 重写用 |
prefix | 空 | 路径前缀 |
root | / | 本地 / FTP / SFTP / WebDAV 根目录 |
key / secret | 空 | Access key / secret |
endpoint / region / bucket | 空 | 对象存储连接 |
字段兼容映射
新项目优先用推荐字段;旧项目兼容历史名:
| 推荐 | 兼容 |
|---|---|
url | public-url / domain |
key | access-key / 腾讯 secret-id |
secret | secret-key |
bucket | bucket-name |
prefix | base-path |
username | user |
root | storage-path |
private-key | private-key-path |
timeout | connection-timeout |
project | project-id |
credentials | credentials-path |
container | container-name |
connection | connection-string |
create-directories 已删除,目录创建是框架内部默认行为。
Provider
| Provider | 用途 |
|---|---|
local-plus | 本地增强;默认被 public / local 使用 |
memory | 内存,测试 / 预览 |
s3 | S3 兼容(AWS S3 / R2 / MinIO 兼容协议) |
aliyun-oss | 阿里云 OSS |
tencent-cos | 腾讯云 COS |
qiniu-kodo | 七牛云 Kodo |
huawei-obs | 华为云 OBS |
minio | MinIO |
baidu-bos | 百度云 BOS |
upyun-uss | 又拍云 USS |
volcengine-tos | 火山引擎 TOS |
azure-blob | Azure Blob Storage |
google-cloud-storage | Google Cloud Storage |
ftp / sftp / webdav | 传统文件服务器 |
业务可见 provider 始终是服务商名称。x-file-storage 是部分 provider 的内部生态实现,不作业务可见 provider 名,不作配置层级。
x-file-storage 还覆盖 FastDFS / GoFastDFS / Mongo GridFS 等专用 / 遗留场景。这些不默认注册,后续按需沉淀为独立 provider 或专用 starter。
内置服务商必填字段
| Provider | 必填 | 可选 |
|---|---|---|
public | root | url / prefix |
local | root | prefix |
memory | — | capacity |
s3 | bucket / region / key / secret | endpoint / url |
aliyun-oss | endpoint / bucket / key / secret | url / prefix |
tencent-cos | region / bucket / key / secret | url / prefix |
qiniu-kodo | bucket / key / secret | url / prefix |
huawei-obs | endpoint / bucket / key / secret | url / prefix |
minio | endpoint / bucket / key / secret | url / prefix |
baidu-bos | endpoint / bucket / key / secret | url / prefix |
upyun-uss | bucket / username / password | url / prefix |
volcengine-tos | endpoint / region / bucket / key / secret | url / prefix |
azure-blob | connection / container | endpoint / url / prefix |
google-cloud-storage | project / credentials / bucket | url / prefix |
ftp | host / username / password | port / root / active / url |
sftp | host / username / password 或 private-key | port / timeout / root / url |
webdav | server / username / password | root / url / prefix |
厂商 SDK
按实际 provider 引入 SDK,不让启动模块强行引入所有云厂商 SDK。
| Provider | 底层 SDK |
|---|---|
s3 | software.amazon.awssdk:s3 |
tencent-cos | com.qcloud:cos_api |
aliyun-oss | com.aliyun.oss:aliyun-sdk-oss |
qiniu-kodo | com.qiniu:qiniu-java-sdk |
huawei-obs | com.huaweicloud:esdk-obs-java |
minio | io.minio:minio |
baidu-bos | com.baidubce:bce-java-sdk |
upyun-uss | com.upyun:java-sdk |
volcengine-tos | com.volcengine:ve-tos-java-sdk |
azure-blob | com.azure:azure-storage-blob |
google-cloud-storage | com.google.cloud:google-cloud-storage |
ftp | 已内置 commons-net / commons-pool2 |
sftp | com.jcraft:jsch |
webdav | com.github.lookfirst:sardine |
需要「全量厂商 SDK 一次性引入」时单独建 provider bundle 模块,不污染 spring-open-application。
自定义 disk / 同服务商多套配置
spring:
open:
storage:
providers:
my-oss:
enabled: true
provider: aliyun-oss
platform: aliyun-oss-main # 必须唯一
endpoint: oss-cn-hangzhou.aliyuncs.com
bucket: your-bucket
key: ${MY_OSS_ACCESS_KEY:}
secret: ${MY_OSS_SECRET_KEY:}Storage.put("my-oss", file) 即可,仍不直接调 x-file-storage API。
扩展点与源码骨架
| 入口 | 文件 | 说明 |
|---|---|---|
| 业务 Facade | Storage | 业务代码的静态短入口,覆盖默认 disk、指定 disk、跨 disk、临时 URL 和内容 URL 重写 |
| 能力管理器 | StorageManager / DefaultStorageManager | 解析默认 disk、选择 Provider、处理跨 disk 复制 / 移动、屏蔽私有文件公开 URL |
| 文件载体 | StorageFile | 统一承载 path、Resource、contentType、原始文件名、大小、metadata 和缩略图选项 |
| 写入结果 | StorageWriteResult | 返回 disk、path、URL、大小、状态码、消息和写入时间;私有 disk URL 在 Manager 层清空 |
| Provider SPI | StorageProvider | 单个 disk / 服务商的原子文件能力扩展点,内置通用复制 / 移动退路 |
| 生态适配 Provider | DelegatingStorageProvider | 把项目 Storage SPI 适配到 x-file-storage 客户端,不把生态库类型泄露给业务模块 |
| 配置解析 | StorageConfigResolver | 从本地配置和 ConfigManager 解析 disk 配置快照,字段 key 由属性 getter 推导为 kebab-case |
| 运行时配置源 | StorageRuntimeConfigSource | Storage 与配置中心之间的窄接口,支持后台配置下一次调用即时生效 |
| 临时签名 | StorageTemporaryUrlSigner / DefaultStorageTemporaryUrlSigner | HMAC 签发和验证私有文件短期访问 URL,签名绑定 disk、path 和过期时间 |
| URL 重写 | StorageUrlRewriter / DefaultStorageUrlRewriter | 按 disk 的历史 URL 前缀重写富文本、纯文本、srcset 和 style URL |
| 媒体资产描述 | StorageMediaAsset / StorageMediaAssets | 把业务旧 URL 字段投影为中性 mediaAssets,提供类型、预览 URL、内容安全 scene 和排序 |
| 服务商客户端工厂 | StorageProviderServiceFactory / DefaultStorageProviderServiceFactory | 隔离底层生态库客户端创建逻辑,可替换官方 SDK 或拆分 provider bundle |
Storage 链路的分层原则:
- 业务模块只依赖
Storage/StorageManager/StorageFile/StorageWriteResult,不直接依赖云厂商 SDK。 StorageManager负责 disk 选择、跨 disk 编排、私有文件 URL 策略和临时签名入口。StorageProvider只处理单个服务商的文件读写、删除、列表、URL 和缩略图,不管理file_record元数据。StorageConfigResolver统一处理数据库覆盖和默认值,Provider 不直接读取配置中心。- 文件业务归属、附件生命周期、下载权限和审计由 Core File / 业务模块处理;Storage starter 只提供底层文件能力。
- x-file-storage 是默认生态实现细节,业务可见 provider 仍是
aliyun-oss、tencent-cos、minio等服务商名。 - 新增服务商优先实现
StorageProvider或扩展StorageProviderServiceFactory,再在自动配置中按条件注册 Bean。
开发期不保留旧配置兼容;Storage 配置、安装向导和后台配置元数据统一使用 default-provider / providers.*。Storage API 的 disk 参数仍表示业务可见存储配置名。
配置示例
MinIO
spring:
open:
storage:
default-provider: minio
providers:
minio:
enabled: true
endpoint: http://localhost:9000
bucket: spring-open
key: ${MINIO_ACCESS_KEY:minioadmin}
secret: ${MINIO_SECRET_KEY:minioadmin}
url: http://localhost:9000/spring-open
prefix: uploads/阿里云 OSS
spring:
open:
storage:
default-provider: aliyun-oss
providers:
aliyun-oss:
enabled: true
endpoint: oss-cn-hangzhou.aliyuncs.com
bucket: ${ALIYUN_OSS_BUCKET:}
key: ${ALIYUN_OSS_ACCESS_KEY:}
secret: ${ALIYUN_OSS_SECRET_KEY:}
url: ${ALIYUN_OSS_URL:}
prefix: uploads/腾讯云 COS
spring:
open:
storage:
default-provider: tencent-cos
providers:
tencent-cos:
enabled: true
region: ap-guangzhou
bucket: spring-open-1250000000 # 含 APPID
key: ${TENCENT_COS_SECRET_ID:}
secret: ${TENCENT_COS_SECRET_KEY:}
url: https://cdn.example.com
prefix: uploads/FTP / SFTP
spring:
open:
storage:
providers:
ftp:
enabled: true
host: ftp.example.com
port: 21
username: ${FTP_USERNAME:}
password: ${FTP_PASSWORD:}
root: /
active: false
sftp:
enabled: true
host: sftp.example.com
port: 22
username: ${SFTP_USERNAME:}
password: ${SFTP_PASSWORD:}
private-key: ${SFTP_PRIVATE_KEY_PATH:} # 建议放 storage/keys
timeout: 5000
root: /数据库配置常用 key
spring.open.storage.default-provider
spring.open.storage.providers.tencent-cos.region
spring.open.storage.providers.tencent-cos.bucket
spring.open.storage.providers.tencent-cos.key
spring.open.storage.providers.tencent-cos.secret
spring.open.storage.providers.tencent-cos.url数据库配置同名 key 优先级 > 环境变量 > application.yml。
当前用户附件 API
普通用户上传头像 / 内容图片 / Blog 封面 / 售后附件等用当前用户接口,不直接调后台文件管理。服务端强制绑定当前登录用户 ID,前端不能伪造归属。
GET /api/v1/file/users/me/records
GET /api/v1/file/users/me/records/{id}
GET /api/v1/file/users/me/records/business
POST /api/v1/file/users/me/records
PUT /api/v1/file/users/me/records/bind
PUT /api/v1/file/users/me/records/unbind
PUT /api/v1/file/users/me/records/business/unbind
DELETE /api/v1/file/users/me/records/{id}上传 multipart/form-data:
| 字段 | 说明 |
|---|---|
file | 必填,上传文件 |
disk | 默认 public |
directory | 默认 uploads(content / after-sale 等) |
businessType | 默认 attachment(blog-cover / forum-content / after-sale 等) |
businessId | 业务对象 ID;草稿可先空 |
visibility | 默认读取 disk 可见性 |
metadata | JSON 字符串或业务扩展 |
业务附件绑定、查询和解绑:
PUT /api/v1/file/users/me/records/bind
GET /api/v1/file/users/me/records/business?businessType=after-sale&businessId=1001&scene=proof
PUT /api/v1/file/users/me/records/unbind
PUT /api/v1/file/users/me/records/business/unbind{
"ids": [1, 2],
"businessType": "after-sale",
"businessId": "1001",
"scene": "proof",
"visibility": "private"
}/users/me/records/unbind 只需要传 { "ids": [1, 2] };/users/me/records/business/unbind 传 businessType、businessId 和可选 scene,用于业务对象删除、草稿回滚或售后撤销时整批解绑。
适合 Chat 附件、Blog / Forum 图片、Mall 售后证明、CMS 图片等场景复用。
后台文件元数据
GET /api/v1/file/records # 分页
GET /api/v1/file/records/{id} # 详情
POST /api/v1/file/records # 创建元数据
PUT /api/v1/file/records/{id} # 修改
POST /api/v1/file/records/temporary/expire # 标记过期临时文件
GET /api/v1/file/records/orphans/audit # 审计孤儿引用
POST /api/v1/file/records/orphans/repair # 修复孤儿引用
POST /api/v1/file/records/expired/cleanup # 清理过期元数据
DELETE /api/v1/file/records/{id} # 删除
DELETE /api/v1/file/records # 批量删除
GET /api/v1/file/records/{id}/preview-url # 预览签名 URL
GET /api/v1/file/records/{id}/download-url # 下载签名 URL
POST /api/v1/file/records/thumbnails/generate # 批量生成图片缩略图
POST /api/v1/content-safety/file-scans # 批量检测图片内容安全
PUT /api/v1/file/records/bind # 批量绑定文件到业务对象
PUT /api/v1/file/records/unbind # 批量解绑业务文件
GET /api/v1/file/records/download # 临时签名访问入口
GET /api/v1/file/storage/config-metadata # 配置表单元数据file_record 只管理元数据,不把二进制塞进数据库。文件长期身份是 disk + path,不是完整 URL;后台返回 url 时按当前 disk 配置动态生成。换 CDN 或对象存储域名时只调 disk url,历史记录不改库。后台文件 VO 会把动态 URL 同步投影到 mediaAssets,用于素材预览、文件类型展示和内容安全 scene 路由;后台分页支持 assetType=image|video|audio|document 和 lifecycleStatus,方便素材库首段按类型、临时 / 已绑定 / 过期状态排查文件。后台 bind / unbind 会先按文件归属做数据范围校验,再把文件生命周期在 temporary 与 bound 之间切换,适合售后凭证、广告素材、商户入驻资料等高风险业务附件复用同一绑定 contract。图片缩略图生成后会写入 metadata.thumbnailUrl,VO 会优先把该值作为媒体资产 thumbnailUrl 返回。图片内容安全批量检测会把轻量结果写入 metadata.contentSafety,包含 action、riskLevel、providerName、providerType、reason、matchCount、scene 和 checkedAt,不保存第三方原始响应或被检测内容。
文件生命周期清理分三步:
temporary/expire:只把过期temporary元数据标记为expired,不删除对象。orphans/audit:只读扫描业务归属缺失或引用数异常的bound元数据,返回missing-business-type、missing-business-id、invalid-reference-count等问题码。orphans/repair:把业务归属缺失或引用数为 0 的bound元数据回退为临时附件。expired/cleanup:清理expired元数据;默认不删除存储对象,显式开启deleteStorageObject时才同步删除底层对象。
自动清理由 spring.open.storage.cleanup.* 控制,默认启用过期标记和孤儿引用修复,物理删除默认关闭。
临时签名 URL
String url = Storage.temporaryUrl("local", "contracts/a.pdf", Duration.ofMinutes(5));
StorageTemporaryUrl info = Storage.temporaryUrlInfo("local", "contracts/a.pdf", Duration.ofMinutes(5));GET /api/v1/file/records/download?disk=local&path=contracts/a.pdf&expires=...&signature=...- 签发前校验数据范围 +
file:record:view - 下载入口不依赖登录态,权限由 HMAC 签名 + 过期 + disk + path 决定
preview-url是「请求预览」:仅对image/jpeg/png/gif/webp/avif/bmp返回inline;SVG / 压缩包 / 表格 / PDF / 未知类型回退attachmentdownload-url始终attachmentdisposition=inline|attachment只是响应头提示,不是权限边界filename=<name>只是下载文件名提示;下载入口会去掉路径分隔符和控制字符,并用 UTF-8Content-Disposition返回,适合中文文件名和导出产物下载- 下载入口按实际
StorageFile.contentType兜底判断 secret生产必须稳定随机;多实例必须一致ttlSeconds1 秒到 7 天,生产 5–10 分钟
缩略图
第一阶段复用 x-file-storage 内置 Thumbnailator,不自研图像处理。
StorageFile file = StorageFile.builder()
.path("avatars/1.png")
.resource(resource)
.contentType("image/png")
.thumbnail(StorageThumbnailOptions.of(120, 120))
.build();
Storage.put("public", file);
String thumbnailUrl = Storage.thumbnailUrl("public", "avatars/1.png");- 只在
thumbnail(...)显式传时生成 - 后缀由
thumbnail-suffix控制,默认.min.jpg thumbnailUrl只返公开 disk URL;私有 disk 不暴露公开地址memory等不支持的 provider 返空字符串- 后台文件记录页的“生成缩略图”会批量扫描图片元数据并回填
metadata.thumbnailUrl;它不会重新上传文件,也不会修改非图片文件。 - 后台文件记录页的“内容检测”会批量扫描图片元数据并回填
metadata.contentSafety;Content Safety Manager 未启用时接口返回跳过计数,不阻断文件页面使用。
内容图片与域名迁移
商品主图、文章封面、富文本图片、站内信附件最容易因 CDN / 对象存储域名 / provider 切换失效。
原则:
- 业务表保存
disk + path,展示时 Storage 生成当前 URL - 富文本可控时保存项目可识别的
disk + path - 已沉淀的绝对 URL 不批量改库;旧域名前缀配
url-aliases,读取时重写
spring:
open:
storage:
providers:
aliyun-oss:
url: https://new-cdn.example.com/assets
url-aliases:
- https://old-cdn.example.com/assets
- https://legacy.example.com数据库配置中用英文半角逗号分割多个旧域名。
业务读取:
String html = Storage.rewriteHtml(article.getContent());
String cover = Storage.rewriteUrl(article.getCoverUrl());
String content = Storage.rewriteText(markdownOrJsonText);rewriteHtml 处理 src / href / poster / data-src / data-original / srcset / data-srcset / style="background-image: url(...)"。
后台配置表单元数据
后台「存储配置页面」不硬编码字段:
GET /api/v1/file/storage/config-metadata返回 configPrefix / configApi / globalFields / builtInDisks / providers / diskTemplates。advanced=true 默认折叠;sensitive=true 后台脱敏展示。
{provider} 是后台自定义 provider 配置名(public / local / tencent-cos / tenant-a-cos)。Storage API 仍把该名称作为 disk 参数使用;同类服务商多实例可不同 provider 配置名,但 platform 必须唯一。
公开目录
public/test.txt → http://localhost:8000/test.txt| 目录 | 用途 |
|---|---|
public/ | 公开静态资源 |
storage/ | 本地存储根目录(私有持久化) |
storage/app/private/ | local 私有 disk 默认根 |
storage/runtime/ | 临时运行文件 |
storage/runtime/logs/ | Spring Boot 日志 |
storage/keys/ | 私钥、证书(不进 Git) |
public/ 不放密钥、证书、私密文件。
开发约定
- 业务表保存
disk + path,不保存当前域名 URL - provider 名走
StorageProviderNames常量;metadata key 走StorageMetadataKeys - 路径会规范化,避免目录穿越
- 对象存储密钥用环境变量 / 后台配置 / 部署平台密钥,不进代码
- 私有文件用
localdisk + 临时签名 URL,不写入public/ - 多厂商优先 x-file-storage 适配 provider 注册,不业务模块手写厂商 SDK 对接
- 缩略图按需
thumbnail(...)触发,不全图自动处理 - 域名 / CDN 切换走
url-aliases+Storage.rewriteHtml(...),不改库
验证命令
./mvnw -pl spring-open-starters/spring-open-starter-storage -am test -DskipITs
./mvnw -pl spring-open-core/spring-open-core-file -am -Dtest='*File*Test' -Dsurefire.failIfNoSpecifiedTests=false test -DskipITs
./mvnw -pl spring-open-application -am test -DskipITs -DskipFrontend参考资料
- Dromara X File Storage 介绍:https://dromara.org/zh/news/x-file-storage-2.0.0.html
- X File Storage 2.2.0 平台支持:https://dromara.org/zh/news/x-file-storage-2.2.0.html
- X File Storage 文档:https://x-file-storage.dromara.org
相关
- Core File(文件元数据后台):module-map.md
- 通知附件(站内信附件 Storage 身份):notification.md
- 配置中心(数据库覆盖):config.md
- 部署与目录约定:deployment.md
- Provider 扩展模型:provider.md