中台保管凭证、签发短时上传会话;浏览器完成SHA-1与分片直传;上传后回传元数据,中台按用户落库。通过能力门控、分片状态机、404拒绝未授权用户,实现安全、可扩展的临时文件分享架构。

一、问题定义

在客户端产品中,「临时传文件」看起来只是一个上传按钮,但工程上至少要同时回答四个问题:

  1. 凭证放哪里:第三方托管 Token 不能进前端包,也不能散落在各业务服务里。

  2. 大文件怎么传:几十 MB 到数 GB 的文件,不能让中台吃满带宽与内存。

  3. 权限怎么切:不是所有登录用户都能用;能力必须可配置、可回收。

  4. 记录归谁:文件落在共享托管账号时,业务侧仍要能按用户维护「我的上传列表」。

最终采用的方案是:

中台(Mono)保管凭证并签发短时上传会话;客户端(Stereo)在浏览器内完成 SHA-1 与分片直传;上传成功后回传元数据,由中台按用户落库。

这不是「再包一层上传代理」,而是一次明确的职责拆分。

二、总体架构

边界原则

系统负责不负责MonoToken 加密存储、会话签发、能力门控、上传记录 CRUD不接收文件字节流,不做分片合并Stereo入口 UI、SHA-1、分片直传、进度、Feed 列表不持久化托管 TokenTmpLink实际对象存储、秒传、分片合并、公开下载链不感知 Stereo 用户身份

一句话:

中台管钥匙与账本,浏览器管搬运,托管方管仓库。

三、为什么不是服务端代理上传

常见方案是 Client → Mid-platform → Object Storage。对小图、受控媒体库这很合理;对「临时分享大文件」则代价过高:

维度服务端代理会话签发 + 浏览器直传中台带宽双倍流量(入站 + 出站)几乎只有控制面请求内存/磁盘需缓冲或落盘浏览器按分片流式读取扩展性受中台出口带宽限制受用户上行带宽限制凭证暴露面Token 不出中台对已授权客户端下发短时 Token(协议约束)实现复杂度服务端简单客户端需实现分片状态机

我选择直传,不是因为「更潮」,而是因为临时大文件的成本中心在带宽,不在业务逻辑

代价也很清楚:第三方协议要求每个分片请求携带账号 Token,因此会话响应对已授权客户端会下发该 Token。工程约束是:

  • 仅内存使用,不写 localStorage

  • 仅对持有 tmplink 能力位的登录用户签发

  • 限流、文件大小上限、URL 域名白名单一并收紧

四、能力门控:入口不是页面,是权限面

Stereo 侧栏入口不硬编码给所有人。它依赖中台的 clientOptions 能力目录:

client_options.tmplink = 「临时传文件」

门控分两层:

  1. 导航层:Stereo 读取 /web/auth/meclientOptions,无 tmplink 则不渲染入口。

  2. API 层/web/tmplink/* 统一校验;无能力返回 404(与私密记事本、图床一致,避免能力探测)。

这保证了:

  • 运营可在后台给用户勾选/取消能力

  • 前端隐藏与后端拒绝一致

  • 未授权用户无法靠猜路径拿到会话

五、上传主链路:从选文件到可分享链接

关键设计点

  1. 先哈希,再会话
    有 SHA-1 才能做秒传判断,也能生成客户端 uptoken

  2. 会话只解决控制面
    Mono 向 TmpLink 换取 utoken 与上传节点,不经手文件体。

  3. 成功后必须回传账本
    托管账号是共享的;业务「谁传的」只能由 Mono 记录回答。

六、分片协议:状态机比「切块 POST」更重要

第三方上传协议的核心不是「把文件切成 3MB」,而是服务端驱动的状态机

客户端本地生成:

uptoken = SHA1( fileSha1 + filename + filesize + slice_size )

随后循环请求上传节点:

POST {uploadServer}/app/upload_slice

状态语义(简化)

状态含义客户端动作1 / 6 / 8完成或秒传/合并完成组装 https://tmp.link/f/{ukey}2等待其他分片短暂等待后继续 prepare3分配下一个分片读取对应 offset,multipart 上传7失败(部分 code 可视为成功)按错误码分支9合并中等待或取 ukey

图片描述

工程实现上的硬约束

  • 分片大小必须与会话一致:续传依赖 slice_size 一致性。

  • 不要整文件进内存算哈希:Stereo 使用增量 SHA-1,按 2MB 块流式更新。

  • 进度要分阶段hashing → session → uploading → done,避免用户以为「卡住」。

  • 循环上限:按预计分片数设置最大循环次数,防止异常状态死循环。

这套协议的本质是:

上传进度的真相在服务端分片账本,不在客户端本地计数器。

客户端本地进度只是 UX 投影。

七、凭证与配置:中台作为唯一信任根

Mono 侧凭证模型对齐现有集成体系(SMTP / Unsplash):

  • 表:tmplink_credentials

  • Token:AES 加密入库,接口仅返回掩码

  • 运行时:DB 启用项优先 → TMPLINK_TOKEN env 兜底

  • 管理面:Setting 卡片维护,权限复用 config:view / config:update

图片描述

业务上使用一把产品级 Token

  • 所有授权用户上传进入同一托管账号空间(mr_id=0

  • 用户隔离不靠托管方目录,而靠 Mono 的 tmplink_uploads.owner_client_user_id

这是有意选择:临时分享场景优先「产品共用配额与运维面」,而不是「一人一托管账号」。

八、上传记录:共享仓库上的个人账本

直传完成后,Stereo 回传:

{
  "filename": "report.pdf",
  "size": 1048576,
  "downloadUrl": "https://tmp.link/f/xxxx",
  "model": 2,
  "sha1": "..."
}

Mono 校验后写入 tmplink_uploads

  • downloadUrl 必须是 https://tmp.link 域名

  • 列表 / 删除一律带 owner_client_user_id

  • 删除记录不删除托管方对象(记录是业务账本,不是对象生命周期控制器)

Stereo 交互按客户端 Feed 设计,而不是后台表格:

  • 顶部:有效期选择 + 选文件 + 进度

  • 下方:卡片流(文件名、大小、有效期、时间、链接)

  • 操作:复制 / 删除

  • 移动端:卡片纵向排布,操作按钮拉满可点区域

  • 滚动:IntersectionObserver 分页加载

图片描述

九、安全模型(必须写进设计,而不是附录)

1. 凭证暴露是协议税,不是疏忽

分片协议要求请求携带账号 Token。接受「已授权客户端可见短时 Token」,同时用以下控制降低风险:

  • 能力门控(tmplink

  • 会话接口限流

  • 单文件上限(当前 2GB)

  • 前端不落盘 Token

  • 记录接口校验下载域名

2. 404 门控优于 403

对未开通能力的用户返回 404,减少「系统有哪些隐藏能力」的信息泄露,并与现有 vault / gallery 语义一致。

3. Owner 隔离是硬条件

所有 files API 的查询与删除都必须带当前 clientUserId。即使知道别人的记录 ID,也不能读写。

4. 记录删除 ≠ 对象删除

产品文案与实现必须一致:删的是「我的分享记录」,不是「从互联网抹掉文件」。若未来要做对象回收,需要额外的托管方删除协议与权限模型。

十、Mono / Stereo 接口契约(精简)

控制面(Mono)

方法路径作用GET/web/tmplink/status是否配置凭证、默认参数POST/web/tmplink/session签发直传会话(可秒传)GET/web/tmplink/files当前用户上传记录POST/web/tmplink/files登记记录POST/web/tmplink/files/delete删除记录

数据面(浏览器 → TmpLink)

步骤目标upload_request_select2由 Mono 代发,换 utoken / 节点prepare_v4秒传检查(可选)prepare / upload_slice浏览器直传分片

Stereo 通过自身 BFF(*.json.ts)转发 Mono 控制面请求,避免把中台地址与鉴权细节散落在页面脚本中。

十一、权衡与后续演进

已接受的权衡

  1. 共用托管账号:运维简单,但配额与风控是产品级的。

  2. 客户端实现协议:Stereo 变重,换来中台轻。

  3. 记录与对象生命周期分离:先保证「可管理的分享列表」,再谈回收。

可演进方向

  1. 会话票据化:若托管方支持更短生命周期的上传凭证,可进一步缩小 Token 暴露窗口。

  2. 异步登记补偿:直传成功但回传失败时,提供本地重试队列。

  3. 对象回收策略:按 model 有效期做业务侧提醒,或对接托管方删除 API。

  4. 上传质量观测:记录平均分片耗时、秒传命中率、失败状态码分布。

十二、结论

这套「临时传文件」不是把第三方 CLI 塞进业务里,而是一次清晰的分层:

  • Mono 成为凭证与权限的信任根,以及用户维度的上传账本。

  • Stereo 成为协议执行器与客户端体验层。

  • TmpLink 成为对象托管与分发层。

当产品需要的是「可授权、可审计、可列表的临时分享」,而不是「中台再当一遍网盘」时,会话签发 + 浏览器分片直传 + 回传落账是更符合成本结构与职责边界的架构。

附录:一次成功上传的最小心智模型

选文件
  → 算 SHA-1
  → 向中台换会话
  →(可秒传)否则按状态机分片直传
  → 得到 tmp.link
  → 回传中台记一笔「我的文件」
  → Feed 可复制 / 可删除记录

架构上真正重要的,不是分片大小取 3MB 还是 5MB,而是:

谁持有密钥,谁执行协议,谁拥有用户语义上的记录。

暂无评论