Files
xiaotutongxue/backend/application-local.yml.example
lovife 65ebf8de90 feat(storage): 对象存储接七牛云 —— 解除"不能上多副本"
文件以前直接写本地磁盘:两个实例各存各的,学生这次传到 A、下次请求落到 B,
文件就"不见了"。这不是性能问题,是正确性问题 —— 它一直卡着不上多副本。

新增:
  · module/file/storage/{StorageService,LocalStorageService,QiniuStorageService}
  · config/StorageConfig(装配 + 可选启动自检)
  · FillProperties 新增 fill.storage
  · MaterialFileController 新增 GET /{id}/url(签名链接)与 GET /{id}/content
  · scripts/e2e-m1-storage.py(存储自检,没配七牛时跳过而非失败)

关键设计:
  ① 业务只认 path(std/1/17/张同学-…-身份证.jpg),各实现自己映射成真实位置:
     本地 ./data/uploads/std/...、七牛 xiaotutongxue/std/...
     **换存储不用改数据库、不用改业务代码**
  ② 空间私有 → 下载必须签名。GET /{id}/url 每次现签(默认 30 分钟),
     所以这个 URL 绝不能长期保存:写进库、塞进导出的 Excel、发给学生明天再打开,都是 403
  ③ 对象键里有中文,拼 URL **必须逐段编码** —— 整串 encode 会把 / 编成 %2F,
     那在七牛那边是另一个键,表现是"传上去了却打不开",且报错什么都没有
  ④ 区域默认 auto:华东-浙江在新旧控制台对应 z0 与 cn-east-2,
     写错了的症状是**上传超时**——一个从报错完全看不出原因的失败
  ⑤ AK/SK 不进仓库也不进 jar:dev.yml 只放占位符;
     真密钥在 backend/application-local.yml(已 gitignore),
     用 spring.config.import: optional:**file**:./application-local.yml
     —— 刻意用 file: 而不是 classpath:,后者会被打进 jar,密钥就跟着构建产物走了
  ⑥ 配了 qiniu 却没密钥 → 启动就炸(fail-fast):起得来的话第一个受害的是
     正在填材料的学生,而排查要从"文件保存失败"一路追到环境变量

已验证:存储抽象改造没破坏任何现有链路 —— 本地磁盘路径下
  e2e-m3-file 29/29、m3-submit 31/31、m1-materials 68/68、m3-draft 27/27 全绿;
  启动日志打出「本地磁盘(多副本部署下会丢文件)」;git check-ignore 确认
  application-local.yml 被忽略而 .example 不被忽略。

⏳ 未验证:**七牛的真实链路没跑过** —— 用户给了域名/空间/区域/私有/目录前缀,
   但没给 AccessKey/SecretKey,没有它生成不了上传凭证。
   补上后跑 python scripts/e2e-m1-storage.py 即可(它会验:配置齐全 →
   上传处理落进七牛 → 取签名链接 → 真的下载回来比字节 → 不带签名被拒
   → 对象键带前缀 → 原始件一字不差)。没配时脚本跳过并 exit 0,不误报失败。
2026-09-29 14:09:13 +08:00

38 lines
2.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ═══════════════════════════════════════════════════════════
# 本地私有配置 —— 模板(这个文件**会**提交,所以只能有占位符)
# ═══════════════════════════════════════════════════════════
#
# 用法:
# 1. 复制成同目录的 application-local.yml(**已在 .gitignore 里,不会被提交**)
# 2. 填上真实的 AccessKey / SecretKey,并把 provider 改成 qiniu
# 3. 重启后端
#
# 为什么要有这么一个文件,而不是直接写进 application-dev.yml:
# application-dev.yml 是提交进仓库的。云存储的 AK/SK 是**有计费与数据权限的真凭据**,
# 与 jwt/crypto 那几把自己生成的密钥不是一回事 —— 后者泄露只影响本系统,
# 前者泄露等于别人能读写整个空间。
#
# 也可以完全不用这个文件,改用环境变量(生产推荐):
# XT_STORAGE_PROVIDER=qiniu
# QINIU_ACCESS_KEY=... / QINIU_SECRET_KEY=...
# QINIU_BUCKET=tools-matrix / QINIU_DOMAIN=https://file.tools.coding.design
#
# 填完想确认配对了,打开启动自检:
# XT_STORAGE_SELF_CHECK=true
# 它会在启动时真的走一遍 写入 → 存在 → 读回 → 删除,
# 把"密钥错 / 空间名错 / 没写权限"当场暴露出来 —— 而不是等第一个学生上传时才失败。
fill:
storage:
provider: qiniu
prefix: xiaotutongxue
qiniu:
access-key: 在这里填 AccessKey
secret-key: 在这里填 SecretKey
bucket: tools-matrix
domain: https://file.tools.coding.design
# auto = 让 SDK 自己查区域(推荐)。华东-浙江在新旧控制台对应 z0 与 cn-east-2,
# 写死了换控制台就错,而错的症状是"上传超时",很难从报错看出原因。
region: auto
signed-url-expire-seconds: 1800