CAS 内容寻址存储:上码的核心技术
很多静态网站托管平台表面上都在做同一件事:
- 上传文件
- 发布网站
- 给你一个链接
但真正决定上传效率、版本管理和回滚体验的,往往不是页面上那个“上传”按钮,而是底层怎么存文件。
上码采用的是 CAS(Content-Addressable Storage,内容寻址存储) 这条思路。它的价值不在于“名字听起来很技术”,而在于它能解决传统静态发布里几个很常见的问题:
- 每次都要重传一堆没变过的文件
- 发布后新旧文件容易混
- 回滚时还得重新找旧包
- 静态资源难以放心做强缓存
什么是 CAS
先把概念讲清楚。
CAS 的核心不是“按文件名存东西”,而是按文件内容存东西。
也就是说,一个文件真正的身份不是:
app.jslogo.pngindex.html
而是它的内容哈希。
在上码当前的实现里,文件会按内容计算 SHA-256。只要内容完全一样,它的哈希就一样;只要内容变了,哪怕只改了一个字符,哈希也会变。
这意味着:
- 同样内容的文件不需要重复存很多份
- 文件一旦写入,就可以当成不可变对象处理
- “路径”和“内容”可以被拆开管理
传统静态上传为什么容易浪费
假设你的网站有 120 个文件:
- 1 个 HTML
- 8 个 CSS / JS
- 100 多张图片和字体
你只改了首页一段文案和一张 banner 图。
如果平台按“整个目录重新传一遍”的方式工作,那你很可能还得重新上传大量其实没变的文件。
这类浪费不只发生在上传时间上,还会影响:
- 带宽消耗
- 发布等待时间
- 回源压力
- 用户拿到新版本的稳定性
对经常更新活动页、作品集、营销站、文档站的人来说,这种低效会很快变成体验问题。
CAS 在上码里是怎么工作的
以上码当前的静态托管链路为例,发布过程可以简化成三步理解。
1. 浏览器先算每个文件的哈希
你上传文件之后,前端不会立刻把所有文件原样推上去,而是会先对每个文件计算 SHA-256。
这一步的意义是:平台先知道“你这次想发的到底是什么内容”,而不是盲目接收一堆同名文件。
2. 服务端只要求上传缺失对象
前端把这次部署的文件清单发给服务端后,服务端会检查:
- 哪些哈希对应的对象已经存在
- 哪些对象是这次真正缺的
如果某个文件内容以前已经传过,即使它今天挂在另一个项目、另一个版本、另一个路径下,它也不需要重新上传。
这就是 CAS 真正带来的“去重”。
3. 最终版本由 manifest 组织起来
对象池里只存“内容”,但用户访问网站时需要的是“路径”。
所以每次部署完成后,还需要一份 manifest 去描述:
{
"index.html": { "hash": "sha256-abc...", "size": 1234 },
"assets/app.js": { "hash": "sha256-def...", "size": 5678 }
}
这份 manifest 的作用不是存内容,而是告诉分发层:
- 这个版本下
- 哪个路径
- 应该指向哪个对象
为什么这对静态网站特别有用
1. 只传真正变化的文件
这是最直观的好处。
如果你只改了一张图和一个 CSS 文件,那就没有必要把整站重新传一遍。
对小站来说,这意味着等待更短;对大站来说,这意味着发布链路更稳。
2. 文件一旦写入,就更适合做强缓存
因为对象是按内容哈希存的,只要哈希不变,内容就不变。
这时平台就可以更放心地给对象配置:
- 长缓存时间
- immutable 响应头
这样 CDN 和浏览器就更容易把静态资源缓存住,而不会担心“同一个 URL 背后内容偷偷变了”。
3. 新版本和旧版本不会天然互相覆盖
这点很关键。
因为路径和内容是通过 manifest 关联的,所以旧版本并不会因为新版本上线而直接被“写没”。
只要旧版本对应的 manifest 还在,旧版本就仍然是一个完整版本,而不是一堆被覆盖过的残片。
4. 回滚更干净
如果你想更深入看这一点,可以结合这篇:网站更新翻车了怎么办?静态网站版本回滚的最短路径。
CAS 对回滚最大的帮助在于:
- 旧对象还在
- 旧 manifest 也还在
- 回滚时不需要重新打一个旧包再上传
这会让回滚动作更接近“切回旧版本指针”,而不是“重新来一遍发布”。
上码当前的存储结构大致是什么样
简化理解,可以看成两层:
R2 Bucket/
├── objects/
│ ├── sha256-abc...
│ └── sha256-def...
└── manifests/
└── <projectId>/
└── <deploymentId>.json
这里最重要的区别是:
objects/存的是不可变内容对象manifests/存的是某次部署版本的路径映射
也就是说,用户访问的是“版本视角”,底层存储的是“内容视角”。
一个更容易理解的例子
假设你的网站里有这些文件:
index.htmlapp.csshero.png
第一次部署时,三者都会进入对象池,并生成版本 A 的 manifest。
第二次部署时,你只修改了 app.css。
这时系统不需要重新上传:
index.htmlhero.png
只需要上传新版本的 app.css,再生成版本 B 的 manifest。
结果就是:
- 对象池只新增了真正变化的内容
- 版本 A 和版本 B 都仍然清楚可辨
- 回退时可以明确回到旧版本
CAS 不是“更快上传”这么简单
很多人第一次听到 CAS,只会把它理解成“秒传”。
这当然是结果之一,但不是全部。
更大的价值其实在于三件事:
1. 发布链路更稳定
因为你减少了不必要的重传,也减少了发布时新旧文件混杂的机会。
2. 分发更容易和缓存策略配合
不可变对象和强缓存天生更搭。
3. 版本管理天然更清晰
当每一次部署都有自己的 manifest,版本这件事就不再依赖“记住当时传了什么文件”。
对使用者来说,最重要的感知是什么
如果你不是在看架构图,而是在真的发网站,你能感受到的变化通常是:
- 第二次、第三次发布更快
- 改一点内容,不用等整站重传
- 资源缓存更稳
- 版本回退更自然
这也是为什么 CAS 特别适合:
- 活动页
- 作品集
- 营销站
- 文档站
- AI 生成网页上线后的持续修改
最后的建议
静态托管平台看起来都在做“上传文件”,但底层怎么存,决定了你后面会不会频繁被这些问题绊住:
- 发布慢
- 版本乱
- 回滚重
- 缓存不稳
CAS 的价值,不在于它是一个很“高级”的技术名词,而在于它把静态网站发布这件事拆得更合理:
- 内容按内容存
- 版本按版本管
- 路径通过 manifest 组织
对最终用户来说,这意味着更快的发布、更干净的版本管理,以及更稳的静态资源分发。