跳到内容

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。

快速开始

java
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:

java
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

java
List<StorageMediaAsset> assets = StorageMediaAssets.ofUrl(
        "cms", "banner", "imageUrl", content.getImageUrl());

如果业务侧已经有缩略图、封面或视频基础 metadata,可以通过 StorageMediaAssetOptions 一起投影,避免各模块重复定义字段口径:

java
StorageMediaAsset video = StorageMediaAssets.fromUrl(
        "mall", "product", "videoUrl", product.getVideoUrl(), 0,
        StorageMediaAssetOptions.video(product.getVideoCoverUrl(), 1920, 1080, 123_000L, "video/mp4"));

StorageMediaAsset 字段口径:

字段说明
consumer消费方,如 cmsblogmall
businessType业务类型,如 bannerarticleproduct
field来源字段,如 imageUrlcoverUrlimageUrls
assetTypeimage / video / audio / document / file
url原始访问 URL
thumbnailUrl / coverUrl图片类资产默认复用原始 URL;非图片为空,后续可由缩略图或视频封面链路填充
contentSafetySceneconsumer.businessType.field 生成的内容安全隔离场景
sortOrder多 URL 字段拆分后的排序
metadata轻量元信息,默认包含 URL 扩展名;视频可附带 posterUrlwidthheightdurationMilliscontentType

这只是读侧 contract,替代文件元数据或素材库。保存长期身份时仍优先使用 disk + pathfile_record 元数据 ID;业务写侧已经生成的缩略图、视频封面和基础 metadata 可以先通过 StorageMediaAssetOptions 暴露给三端。Core File 文件元数据已返回 mediaAssets,Admin 文件记录页可展示素材预览和内容安全 scene,并支持按素材类型、生命周期、业务归属筛选;该页也可触发图片文件缩略图生成 / 回填,以及图片文件内容安全批量检测 / 回填。后台 /api/v1/file/records/bind/api/v1/file/records/unbind 已提供文件记录业务绑定 / 解绑写侧 contract;Admin 上传体验页已接入当前用户业务文件绑定、查询和解绑,素材库独立选择器继续按后续功能簇推进。

指定 disk 同名重载:

java
Storage.putString("tencent-cos", "docs/readme.txt", "hello");
Storage.copy("tencent-cos", "docs/readme.txt", "archive/readme.txt");
Storage.files("tencent-cos", "docs");

跨 disk 复制 / 移动:

java
Storage.copy("public", "uploads/a.png", "tencent-cos", "images/a.png");
Storage.move("public", "uploads/b.png", "aliyun-oss", "images/b.png");
  • 同 disk 优先走底层原生能力;不可用时回退 read + putcopy + delete
  • 跨 disk 由框架统一 source get → target put;移动 = 复制 + 删除源
  • 对象存储「目录」通常是逻辑前缀
  • public / local 都用 local-plus,具备分片、列表、复制 / 移动、缩略图

配置项

yaml
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

通用参数

配置默认值说明
enabledtrue启用开关
default-providerpublic默认 provider 配置名;Storage API 中仍可把该名称作为 disk 参数使用
register-common-providerstrue自动注册常见 provider
thumbnail-suffix.min.jpg缩略图后缀
temporary-url.enabledtrue临时签名 URL
temporary-url.route/api/v1/file/records/download临时签名下载路由
temporary-url.ttl10m默认 TTL
temporary-url.secret生产必须配置
url-rewrite.enabledtrue内容 URL 重写
config.enabledtrue数据库覆盖

Provider 公共字段

字段默认说明
enabledtrue启停
provider<provider>真实 provider
platform<provider>底层平台标识
visibilitypublicprivate 时不生成公开 URL
url公开 URL 前缀 / CDN 域名
url-aliases历史 URL 前缀,URL 重写用
prefix路径前缀
root/本地 / FTP / SFTP / WebDAV 根目录
key / secretAccess key / secret
endpoint / region / bucket对象存储连接

字段兼容映射

新项目优先用推荐字段;旧项目兼容历史名:

推荐兼容
urlpublic-url / domain
keyaccess-key / 腾讯 secret-id
secretsecret-key
bucketbucket-name
prefixbase-path
usernameuser
rootstorage-path
private-keyprivate-key-path
timeoutconnection-timeout
projectproject-id
credentialscredentials-path
containercontainer-name
connectionconnection-string

create-directories 已删除,目录创建是框架内部默认行为。

Provider

Provider用途
local-plus本地增强;默认被 public / local 使用
memory内存,测试 / 预览
s3S3 兼容(AWS S3 / R2 / MinIO 兼容协议)
aliyun-oss阿里云 OSS
tencent-cos腾讯云 COS
qiniu-kodo七牛云 Kodo
huawei-obs华为云 OBS
minioMinIO
baidu-bos百度云 BOS
upyun-uss又拍云 USS
volcengine-tos火山引擎 TOS
azure-blobAzure Blob Storage
google-cloud-storageGoogle Cloud Storage
ftp / sftp / webdav传统文件服务器

业务可见 provider 始终是服务商名称。x-file-storage 是部分 provider 的内部生态实现作业务可见 provider 名,作配置层级。

x-file-storage 还覆盖 FastDFS / GoFastDFS / Mongo GridFS 等专用 / 遗留场景。这些默认注册,后续按需沉淀为独立 provider 或专用 starter。

内置服务商必填字段

Provider必填可选
publicrooturl / prefix
localrootprefix
memorycapacity
s3bucket / region / key / secretendpoint / url
aliyun-ossendpoint / bucket / key / secreturl / prefix
tencent-cosregion / bucket / key / secreturl / prefix
qiniu-kodobucket / key / secreturl / prefix
huawei-obsendpoint / bucket / key / secreturl / prefix
minioendpoint / bucket / key / secreturl / prefix
baidu-bosendpoint / bucket / key / secreturl / prefix
upyun-ussbucket / username / passwordurl / prefix
volcengine-tosendpoint / region / bucket / key / secreturl / prefix
azure-blobconnection / containerendpoint / url / prefix
google-cloud-storageproject / credentials / bucketurl / prefix
ftphost / username / passwordport / root / active / url
sftphost / username / passwordprivate-keyport / timeout / root / url
webdavserver / username / passwordroot / url / prefix

厂商 SDK

按实际 provider 引入 SDK,让启动模块强行引入所有云厂商 SDK。

Provider底层 SDK
s3software.amazon.awssdk:s3
tencent-coscom.qcloud:cos_api
aliyun-osscom.aliyun.oss:aliyun-sdk-oss
qiniu-kodocom.qiniu:qiniu-java-sdk
huawei-obscom.huaweicloud:esdk-obs-java
minioio.minio:minio
baidu-boscom.baidubce:bce-java-sdk
upyun-usscom.upyun:java-sdk
volcengine-toscom.volcengine:ve-tos-java-sdk
azure-blobcom.azure:azure-storage-blob
google-cloud-storagecom.google.cloud:google-cloud-storage
ftp已内置 commons-net / commons-pool2
sftpcom.jcraft:jsch
webdavcom.github.lookfirst:sardine

需要「全量厂商 SDK 一次性引入」时单独建 provider bundle 模块,污染 spring-open-application

自定义 disk / 同服务商多套配置

yaml
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。

扩展点与源码骨架

入口文件说明
业务 FacadeStorage业务代码的静态短入口,覆盖默认 disk、指定 disk、跨 disk、临时 URL 和内容 URL 重写
能力管理器StorageManager / DefaultStorageManager解析默认 disk、选择 Provider、处理跨 disk 复制 / 移动、屏蔽私有文件公开 URL
文件载体StorageFile统一承载 path、Resource、contentType、原始文件名、大小、metadata 和缩略图选项
写入结果StorageWriteResult返回 disk、path、URL、大小、状态码、消息和写入时间;私有 disk URL 在 Manager 层清空
Provider SPIStorageProvider单个 disk / 服务商的原子文件能力扩展点,内置通用复制 / 移动退路
生态适配 ProviderDelegatingStorageProvider把项目 Storage SPI 适配到 x-file-storage 客户端,不把生态库类型泄露给业务模块
配置解析StorageConfigResolver从本地配置和 ConfigManager 解析 disk 配置快照,字段 key 由属性 getter 推导为 kebab-case
运行时配置源StorageRuntimeConfigSourceStorage 与配置中心之间的窄接口,支持后台配置下一次调用即时生效
临时签名StorageTemporaryUrlSigner / DefaultStorageTemporaryUrlSignerHMAC 签发和验证私有文件短期访问 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-osstencent-cosminio 等服务商名。
  • 新增服务商优先实现 StorageProvider 或扩展 StorageProviderServiceFactory,再在自动配置中按条件注册 Bean。

开发期不保留旧配置兼容;Storage 配置、安装向导和后台配置元数据统一使用 default-provider / providers.*。Storage API 的 disk 参数仍表示业务可见存储配置名。

配置示例

MinIO

yaml
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

yaml
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

yaml
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

yaml
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

text
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,前端能伪造归属。

text
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默认 uploadscontent / after-sale 等)
businessType默认 attachmentblog-cover / forum-content / after-sale 等)
businessId业务对象 ID;草稿可先空
visibility默认读取 disk 可见性
metadataJSON 字符串或业务扩展

业务附件绑定、查询和解绑:

http
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
json
{
  "ids": [1, 2],
  "businessType": "after-sale",
  "businessId": "1001",
  "scene": "proof",
  "visibility": "private"
}

/users/me/records/unbind 只需要传 { "ids": [1, 2] }/users/me/records/business/unbindbusinessTypebusinessId 和可选 scene,用于业务对象删除、草稿回滚或售后撤销时整批解绑。

适合 Chat 附件、Blog / Forum 图片、Mall 售后证明、CMS 图片等场景复用。

后台文件元数据

text
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|documentlifecycleStatus,方便素材库首段按类型、临时 / 已绑定 / 过期状态排查文件。后台 bind / unbind 会先按文件归属做数据范围校验,再把文件生命周期在 temporarybound 之间切换,适合售后凭证、广告素材、商户入驻资料等高风险业务附件复用同一绑定 contract。图片缩略图生成后会写入 metadata.thumbnailUrl,VO 会优先把该值作为媒体资产 thumbnailUrl 返回。图片内容安全批量检测会把轻量结果写入 metadata.contentSafety,包含 actionriskLevelproviderNameproviderTypereasonmatchCountscenecheckedAt,不保存第三方原始响应或被检测内容。

文件生命周期清理分三步:

  • temporary/expire:只把过期 temporary 元数据标记为 expired,不删除对象。
  • orphans/audit:只读扫描业务归属缺失或引用数异常的 bound 元数据,返回 missing-business-typemissing-business-idinvalid-reference-count 等问题码。
  • orphans/repair:把业务归属缺失或引用数为 0 的 bound 元数据回退为临时附件。
  • expired/cleanup:清理 expired 元数据;默认不删除存储对象,显式开启 deleteStorageObject 时才同步删除底层对象。

自动清理由 spring.open.storage.cleanup.* 控制,默认启用过期标记和孤儿引用修复,物理删除默认关闭。

临时签名 URL

java
String url = Storage.temporaryUrl("local", "contracts/a.pdf", Duration.ofMinutes(5));
StorageTemporaryUrl info = Storage.temporaryUrlInfo("local", "contracts/a.pdf", Duration.ofMinutes(5));
text
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 / 未知类型回退 attachment
  • download-url 始终 attachment
  • disposition=inline|attachment 只是响应头提示,是权限边界
  • filename=<name> 只是下载文件名提示;下载入口会去掉路径分隔符和控制字符,并用 UTF-8 Content-Disposition 返回,适合中文文件名和导出产物下载
  • 下载入口按实际 StorageFile.contentType 兜底判断
  • secret 生产必须稳定随机;多实例必须一致
  • ttlSeconds 1 秒到 7 天,生产 5–10 分钟

缩略图

第一阶段复用 x-file-storage 内置 Thumbnailator,自研图像处理。

java
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,读取时重写
yaml
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

数据库配置中用英文半角逗号分割多个旧域名。

业务读取:

java
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(...)"

后台配置表单元数据

后台「存储配置页面」硬编码字段:

http
GET /api/v1/file/storage/config-metadata

返回 configPrefix / configApi / globalFields / builtInDisks / providers / diskTemplatesadvanced=true 默认折叠;sensitive=true 后台脱敏展示。

{provider} 是后台自定义 provider 配置名(public / local / tencent-cos / tenant-a-cos)。Storage API 仍把该名称作为 disk 参数使用;同类服务商多实例可不同 provider 配置名,但 platform 必须唯一。

公开目录

text
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
  • 路径会规范化,避免目录穿越
  • 对象存储密钥用环境变量 / 后台配置 / 部署平台密钥,进代码
  • 私有文件用 local disk + 临时签名 URL,写入 public/
  • 多厂商优先 x-file-storage 适配 provider 注册,业务模块手写厂商 SDK 对接
  • 缩略图按需 thumbnail(...) 触发,全图自动处理
  • 域名 / CDN 切换走 url-aliases + Storage.rewriteHtml(...)改库

验证命令

bash
./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

参考资料

相关

Released under the Apache License 2.0.