附件管理基础设施的设计与实践:从旧版组件到 lcxm-attachment

原创 临窗旋墨 2026-09-04 阅读:3 分类: java后端 专题: 基础设施 标签: springboot

附件管理基础设施的设计与实践:从旧版组件到 lcxm-attachment

最后更新:2026-09-04

一、背景与演进

之前写过一个 attachment 组件,支持两种使用方式:

  • 一种是 client mode,由业务应用通过 starter 调用独立附件服务;
  • 另一种是 server mode,由业务应用自己提供附件服务并完成本地存储。

旧版本已经解决了基本上传问题,但图片压缩、缩略图、访问鉴权和生命周期管理比较分散。现在的主要使用场景是微信小程序和多个业务应用,因此重新整理了文件资源、业务关系、访问权限、状态流转和部署边界。

当前版本优先采用“独立 attachment-server + 业务 starter”的 client mode。
业务应用不代理文件流,附件服务只负责文件资源;server mode 暂不放入主流程,后续如果出现离线部署或单体应用场景,再通过独立 adapter 评估。

二、总体认识

2.1 参与者和边界

text 复制代码
+------------+     +----------------------+     +----------------------+
| Client     |     | Business Application |     | Attachment Server    |
| Mini App   |     | API + Starter        |     | Upload/Access/Tasks  |
+-----+------+     +----------+-----------+     +----------+-----------+
      |                       |                           |
      | upload-info/form      | relation/status           |
      +---------------------->|                           |
      |                       | direct upload             |
      +-------------------------------------------------->|
      |                       |                           |
      |                       v                           v
      |                +-------------+             +-------------+
      |                | Business DB |             | Server DB   |
      |                | relations   |             | attachments |
      |                +-------------+             | variants    |
      |                                            +------+------+ 
      |                                                   |
      |                                                   v
      |                                            +-------------+
      |                                            | File Volume |
      |                                            +-------------+

客户端负责选择上传策略、直传文件和提交附件关系。
业务应用及 starter 负责用户权限、业务事务、关系维护和详情回填。attachment-server 负责上传校验、文件存储、图片处理、访问鉴权、派生资源和清理任务。
业务库只保存业务关系,server 库保存附件元数据和派生资源状态。

2.2 一条附件的完整生命周期

text 复制代码
获取 upload-info
      -> 客户端携带 JWT 直传
      -> server 校验路径、应用、MIME 和图片内容
      -> 按策略写入主文件,状态 INIT
      -> 业务表单提交附件关系
      -> 业务事务提交后同步为 USED
      -> 按需创建或生成 THUMBNAIL_320
      -> 详情根据场景返回主图或缩略图访问地址
      -> 关系移除后同步为 DELETED
      -> 清理任务删除主文件、派生文件和元数据

三、端到端流程

3.1 上传和绑定

text 复制代码
CLIENT              BUSINESS APP/STARTER       ATTACHMENT SERVER       BUSINESS DB
  |                         |                         |                    |
  |-- upload-info --------->|                         |                    |
  |<-- uploadUrl/token -----|                         |                    |
  |                         |                         |                    |
  |-- multipart + JWT + policy ---------------------->|                    |
  |                         |                         |-- verify JWT       |
  |                         |                         |-- verify app/path  |
  |                         |                         |-- inspect MIME     |
  |                         |                         |-- compress/resize  |
  |                         |                         |-- write primary    |
  |                         |                         |-- insert INIT      |
  |<-- attachmentId/key ------------------------------|                    |
  |                         |                         |                    |
  |-- submit business form -->|                       |                    |
  |                         |-- save entity + relation ------------------->|
  |                         |<-- transaction committed --------------------|
  |                         |-- sync INIT -> USED ---->|                   |

上传信息中可以指定 COMPRESS_720ORIGINAL。客户端默认策略由 starter 配置决定,server 在策略为空时默认使用 COMPRESS_720。上传阶段先保存主文件并登记 INIT,只有业务关系成功提交后才转为 USED,避免未绑定文件长期占用空间。

3.2 访问和派生资源

text 复制代码
CLIENT              BUSINESS APP/STARTER       ATTACHMENT SERVER       SERVER DB/FILES
  |                         |                         |                    |
  |-- request detail ------>|                         |                    |
  |                         |-- query relation        |                    |
  |                         |-- choose PRIMARY/THUMB  |                    |
  |<-- accessUrl -----------|                         |                    |
  |                         |                         |                    |
  |-- private URL --------->|                         |                    |
  |                         |-- check user permission |                    |
  |                         |-- issue access JWT ---->|                    |
  |<-- redirect URL --------|                         |                    |
  |-------------------------------------------------->|                    |
  |                         |                         |-- verify JWT       |
  |                         |                         |-- resolve objectKey|
  |                         |                         |-- find variant     |
  |                         |                         |-- claim PROCESSING |
  |                         |                         |-- generate if miss |
  |                         |                         |-- mark READY       |
  |<--------------------------------------------------|-- file response ---|

public 附件可以直接访问;private 附件必须先经过业务应用的用户权限判断,再使用短期 JWT 访问地址。缩略图采用确定性 objectKey,例如 abc.jpg 对应 abc_thumbnail_320.jpg。历史附件没有缩略图记录时,可以在访问或低频任务中延迟生成。

3.3 删除和清理

text 复制代码
BUSINESS APP/STARTER       BUSINESS DB              ATTACHMENT SERVER       CLEANER
        |                         |                         |                    |
        |-- remove relation ----->|                         |                    |
        |<-- transaction commit --|                         |                    |
        |-- sync USED -> DELETED -------------------------->|                    |
        |                         |                         |                    |
        |                         |                         |<-- scheduled scan -|
        |                         |                         |-- verify state/path|
        |                         |                         |-- delete variants -|
        |                         |                         |-- delete primary --|
        |                         |                         |-- delete metadata -|

清理任务低频运行,用于兜底处理超时 INITDELETED 数据;派生文件和主文件在同一生命周期内清理。删除关系不需要先填充详情,业务端直接根据表单中的关系差异处理即可。

四、模块职责

text 复制代码
lcxm-attachment-core                 shared DTO, entity, enum, exception, utility
lcxm-attachment-server               upload, storage, auth, image, variant, cleanup
lcxm-attachment-spring-boot-starter  relation, status sync, fill, private redirect

业务端通过 AttachmentOperationHelper 维护新增、保留和移除关系,通过 AttachmentFillHelper 在详情返回阶段按需填充访问地址。accessUrl 只用于响应,不写入数据库。

五、图片处理策略

当前主图策略包括:

  • COMPRESS_720:支持的图片按比例缩放,使最大边不超过 720;JPEG 通过质量迭代和尺寸回退控制体积,目标约 300KB、硬上限约 400KB。
  • ORIGINAL:保留客户端文件内容,但仍执行真实格式、MIME、尺寸和路径安全校验。
  • THUMBNAIL_320:按比例生成最大边 320 的列表和时间轴缩略图。

PNG 不使用 JPEG 的质量参数,会按比例缩放并保留透明通道。压缩结果与图片内容有关,一次实际测试中,原图 2.72MB 处理为 43.4KB 主图,THUMBNAIL_32012.8KB;这不是固定压缩比例。

text 复制代码
upload stream
      -> controlled temporary file
      -> detect real image format and dimensions
      -> validate request MIME and actual MIME
      -> resize/compress according to enum policy
      -> write primary objectKey
      -> create pending variant metadata when needed
      -> remove temporary file

派生资源记录使用 PENDING -> PROCESSING -> READY/FAILED 状态。多个实例同时处理时先原子领取任务,只有领取成功的实例可以生成文件;生成完成后再更新状态,失败则保留可重试信息。

六、JWT、访问安全与业务关系

JWT 用于证明调用方的应用身份、上传参数或私有资源访问范围,不替代业务用户权限。server 会校验 token 中的 appCode、路径中的 appCode、客户端配置和请求参数是否一致,所有 objectKey 入口都进行路径安全校验。

text 复制代码
业务用户权限判断(Business App)
          -> 生成短期访问 JWT(Starter)
          -> server 校验 token、appCode、objectKey 和 variant
          -> 返回文件或重定向

附件资源与业务数据采用最终一致模型:业务库保存 t_attachment_relation,server 库保存 t_attachmentt_attachment_variantt_client_config,两侧通过 attachmentId/objectKey 关联,不建立跨库强事务或外键。

七、部署与 CI/CD

text 复制代码
Cloud production : prod / lcxm_attachment       /xqd/attachments/prod
Home test        : test / lcxm_attachment_test  /xqd/attachments/test
Local development: dev

微信小程序正式访问要求 HTTPS 和合法域名,因此生产附件服务部署在云主机更合适;小主机保留 Docker 测试环境。Docker 只是运行方式,不是 Spring profile。Jenkins 在小主机构建镜像,默认发布 test;将 DEPLOY_CLOUD=true 时,再通过 docker savescp 和云端 docker load 发布 prod。数据库、文件卷和备份必须保持同一环境边界,避免测试数据覆盖生产资源。

带宽和流量限制需要结合访问量评估。主图统一压缩、缩略图用于列表、private 资源经过权限校验,可以显著降低小程序访问流量;后续接入 CDN 或对象存储时,再补充缓存和跨地域同步策略。

八、当前未完成能力

当前 server 使用本地文件存储。MinIO、Cloudflare R2、OSS 等对象存储同步,以及同步重试、幂等、完整性校验、恢复策略尚未完成。文件 hash/秒传、分片上传、CDN 和管理后台属于后续扩展。

总结

text 复制代码
业务应用负责业务数据和用户权限
starter 负责业务关系、状态同步和详情填充
attachment-server 负责文件资源、图片处理、鉴权和清理

当前有效配置、接口和接入步骤以项目 docs/ 下的正式文档为准。

附录:代码与相关文档

A.1 代码与基础设施

A.2 当前项目文档

评论区

avatar
未登录

暂无评论,来发表第一条评论吧~