只存 object key,不存 URL
AI 摘要
内容管理系统中存 URL 而非 object key,会在更换存储商或 CDN 时引发连锁重写问题。核心方案是将 URL 生成与内容存储解耦:内容只存供应商无关的 object key,渲染时动态组装 URL,换基础设施只需改提供层一行代码。
规则一句话:内容里存 object key,URL 在渲染时生成。
错误:https://pub-xxxx.r2.dev/blog/2026/09/architecture.png
正确:blog/2026/09/architecture.png
为什么
URL 里含三样会变的东西:域名、路径前缀、访问凭据形式。
- 换存储商 → 域名变
- 换 CDN → 域名变
- 从公开桶改成签名访问 → 整条 URL 的结构都变
而 object key 是你自己的命名空间,跟供应商无关。它只在你重新组织内容结构时才需要变——那是你主动做的决定,不是被动挨的刀。
破规则的方式
这条规则简单到看起来不需要说,但它有几种很常见的破法:
1. 上传工具自动填了完整 URL。
后台上传完,工具很贴心地把可访问的完整链接显示在界面上,你复制粘贴进文章——破了。
2. 图床。
用第三方图床,拿到的是图床的短链。这时候你不但丢了 key,连对象都不在你手上。
3. Markdown 编辑器拖图片进去。
拖进去的瞬间,编辑器把图片上传到某个地方然后插入完整 URL。
共同点是:都是“顺手”的操作。所以我不用规则约束自己,用结构约束——上传入口只有一个,它只返回 key,压根不给你 URL。
代码上怎么保证
export interface StorageProvider {
upload(key: string, body: ArrayBuffer, contentType: string): Promise<void>;
delete(key: string): Promise<void>;
/** key → 可访问 URL,这是唯一生成 URL 的地方 */
getUrl(key: string): string;
}
业务层拿到的永远是 key。要显示时调 getUrl(key)。
这样换存储商的时候,改动范围是:一个新的 Provider 实现 + 工厂函数里的一行。
const storage = createStorageProvider(
import.meta.env.STORAGE_PROVIDER ?? "local",
{ /* 凭据 */ }
);
例外
有一种情况可以直接存 URL:纯外链。比如引用别人网站的图片、嵌入外部视频。这些对象本来就不在你手上,存 key 没有意义——但也要意识到,它们可能哪天就 404 了。
对我自己的站点,做法是:外链图片在写文章时下载一份存进自己的存储,然后存 key。多花三十秒,换十年后链接还活着。
这是一条笔记,不是成稿文章。它可能不完整、可能有错,也可能过段时间就被我推翻。 想看成体系的文章,去 Blog。