试想一下,你的静态博客跑了大半年,文章越写越多,图片也越来越多。
某天你注意到一个问题:站点仓库已经 800MB 了,git clone 要等两分钟,Vercel 构建时间从 30 秒涨到了 3 分钟。你翻了一下站点文件,绝大部分体积都是图片。
更尴尬的是,来自东南亚的读者可能会反馈:你的文章写得很好,但站点、图片都加载太慢了,经常转半天圈才出来。
问题的根源很简单——图片和站点代码混在一起。每次部署把全部内容推到一个仓库,图片没有经过 CDN 加速,全球用户都在从同一个源站拉取。
所以有没有好的办法可以解决呢?
有的。CDN 加速。
这篇文章聊聊 Gridea Pro CDN 这个功能的设计思路、核心决策,以及我们对「好的图片管理体验」的理解。
一、有没有 CDN,到底差在哪
先澄清一个认知:没有 CDN,图片也是能正常显示的。 部署时图片会跟着 HTML 一起推到托管平台,访问时不会加载不出来图片,只是很慢,当然有时候也可能裂图。
那 CDN 解决的是什么?
两件事:
第一,仓库体积。 静态站点仓库应该只放生成后的 HTML/CSS/JS,这些都是文本,压缩后很小。但图片是二进制文件,一张截图几百 KB 到几 MB,累积下来仓库会迅速膨胀。仓库越大,clone 越慢,CI/CD 构建越慢,协作体验越差。
第二,访问速度。 GitHub Pages 或 Vercel 的服务器集中在少数几个区域。没有 CDN 时,一个深圳的读者和一个巴西的读者都从同一个源站拉图片,延迟差异巨大。CDN 把图片分发到全球节点,用户从最近的节点加载,速度可以快几倍甚至十几倍。
所以 CDN 图床的本质是把图片从站点仓库中剥离出来,存到一个专门的地方,通过全球加速网络分发。
听起来不复杂,但对普通用户来说,手动操作这套流程意味着:
- 创建一个 GitHub 仓库专门存图片
- 每次新增图片后手动上传到这个仓库
- 记住哪些图片已上传、哪些还没有
- 确保 HTML 里的图片路径正确指向 CDN 地址
但是,很麻烦,对吧?
这就不应该是用户操心的事。
二、我们想要的最终体验
在动手之前,我们先定义了理想的用户体验:
用户只需要做一次配置(填个 Token、选个路径模式),之后每次部署时,图片自动上传到 CDN 仓库,HTML 中的路径自动替换为 CDN 地址,全程无感。
拆解下来,有四件事要做:
- CDN 配置界面:让用户填写 GitHub 仓库信息、Token、选择路径模式
- 自动上传:部署时自动扫描并上传媒体文件到 CDN 仓库
- URL 重写:渲染 HTML 时把
/post-images/xx替换为https://cdn.jsdelivr.net/gh/user/repo@main/post-images/xx - 测试验证:配置完就能立即测试,不用等到部署才发现配错了
同时有一个硬约束:本地预览不能受影响。预览时图片必须继续走本地路径,只有部署上线后才使用 CDN 地址。
为什么这么做呢?就是为了本地预览时更快,部署到线上时自动经过 CDN 加速。
三、设计过程中的几个关键决策
决策一:路径模板,给用户可控的灵活度
图片上传到 CDN 仓库后放在哪?全堆在根目录显然不行,文件多了就是灾难。
我们参考了 uPic、PicGo 等成熟图床工具的做法,设计了路径模板变量系统。用户可以用变量组合出自己想要的目录结构:
| 变量 | 含义 | 示例 |
|---|---|---|
{year} |
当前年份 | 2026 |
{month} |
当前月份 | 03 |
{day} |
当前日期 | 12 |
{filename} |
不含扩展名的文件名 | cover |
{.suffix} |
带点的扩展名 | .png |
{random} |
8 位随机字符串 | a1b2c3d4 |
预设了 4 种常用模式,覆盖大多数人的需求:
{year}/{month}/{filename}{.suffix} → 2026/03/cover.png
{year}/{month}/{day}/{filename}{.suffix} → 2026/03/12/cover.png
blog/images/{year}/{month}/{filename}{.suffix} → blog/images/2026/03/cover.png
{year}/{filename}-{random}{.suffix} → 2026/cover-a1b2c3d4.png
也留了「自定义」选项,高级用户可以随意组合。
这样做的好处是:对于只想快速配好的用户,选一个预设就行;对于有强迫症的用户,路径完全可控。
决策二:批量上传保持原始结构——一个差点踩进去的坑
最初我们的想法是:部署时批量上传也用路径模板来决定每个文件的远程路径。但仔细推演了一遍,发现有严重问题——
假设模板是 {year}/{month}/{filename}{.suffix}。1 月份部署时图片传到了 2026/01/ 下,CDN 地址也带上了 2026/01 前缀。到了 3 月份再部署,前缀变成 2026/03,所有旧文章里的图片链接全部失效。
这是个很隐蔽的坑。如果没有提前想到,等用户用了几个月才发现旧图片打不开,那就是灾难性的体验问题。
最终方案:批量部署上传时,完全保持原始目录结构(post-images/xx、images/xx、media/xx),直接映射到 CDN 仓库根目录。路径模板只用在测试上传和未来的编辑器内单图上传场景。
这样,无论何时部署,/post-images/cover.png 永远对应同一个 CDN 地址,路径稳定,不受时间影响。
决策三:智能去重,不做无用功
每次部署都会扫描所有媒体目录,但绝大多数文件上次已经上传过了。如果每次都重新上传,既浪费时间又浪费 API 配额。
我们的做法是:上传前先计算本地文件的内容指纹,与远程仓库中的文件指纹比对。内容相同则跳过,内容不同或文件不存在才上传。
这意味着,即使你的博客有 200 张图片,如果这次只新增了 3 张,实际只会上传 3 个文件。部署速度几乎不受历史图片数量的影响。
决策四:单文件失败不中断部署
部署是用户等了很久才触发的操作。如果因为一张图片上传失败(比如网络抖动)就整个中断,让用户重新来过,体验太差了。
我们选择了柔性策略:单个文件上传失败只记录日志继续执行,不中断部署流程。用户可以在部署日志中看到哪些文件失败了,下次部署时会自动重试(因为去重机制检测到文件不存在,会重新上传)。
决策五:有并发但克制
多个文件可以并行上传,提升速度。但并发不是越多越好——GitHub API 有速率限制,短时间内请求过密会被拒绝。
我们设定了 5 并发的上限。这个数字是在速度和稳定性之间找到的平衡点:比串行快了数倍,但不至于触发限流。
四、完整的工作流程
配置完成后,用户的日常写作体验是这样的:
1. 打开编辑器,写文章,拖入图片
→ 图片保存到本地 post-images/ 目录
2. 点击预览
→ 直接读本地文件,图片正常显示,毫秒级响应
3. 点击部署(这是唯一需要做的操作)
① 渲染静态站点
→ HTML 中的图片路径自动替换为 CDN 地址
② 扫描媒体目录,智能上传
→ 新文件上传到 CDN 仓库
→ 已存在且内容相同的文件自动跳过
→ 5 并发,失败不中断
③ 推送 HTML 到托管平台
4. 用户访问线上站点
→ 图片从 jsDelivr CDN 全球节点加载,速度飞快
全程只需要一次点击。没有手动上传,没有路径拼接,没有遗漏担忧。
五、前端体验的细节打磨
功能做对了只是及格,体验做好了才是满分。我们在 UI 上也花了一些心思。
Token 输入框:默认密码遮盖,旁边有眼睛图标切换可见性。一个小细节,但在演示或录屏时能避免泄露敏感信息。
保存路径选择:下拉框提供 4 个预设方案,底部有「自定义」选项。选择自定义后展开输入框,下方提示支持的变量列表。既照顾新手,也给老手留空间。
测试上传按钮:配置完点一下,立即上传一个测试文件到 CDN 仓库,成功后 toast 显示完整的 CDN 访问 URL。先保存配置再测试,保证测试用的是最新配置。按钮只在满足条件时显示(CDN 已启用 + Token 已填写),避免无效操作。
提示信息:用主题色柔和呈现,简明扼要地告知用户「部署时将自动上传媒体文件」,不制造焦虑,给人信心。
六、架构上的考量
在 Gridea Pro 的分层架构中,CDN 功能横跨了多个层次:
- 领域层定义了 CDN 配置的数据结构(仓库信息、Token、路径模板)
- 仓库层负责配置的持久化存储(JSON 文件)
- 服务层包含两个核心能力:上传服务(文件扫描、去重、并发上传)和渲染引擎中的 URL 重写
- 外观层将上传能力暴露给前端(测试上传接口)
- 部署流程在渲染和推送之间编排上传步骤
这样的分层让每个职责都清晰独立。URL 重写不关心文件怎么上传,上传服务不关心 HTML 怎么渲染,部署流程只负责编排顺序。未来如果要支持更多 CDN 提供商(比如阿里云 OSS、七牛云),只需要在上传服务层新增实现,其他层完全不用动。
七、为什么不在编辑器里实时上传
有人可能会问:为什么不在编辑器里粘贴图片时就实时上传到 CDN?
这确实是更理想的体验,PicGo 就是这么做的。但我们有意把它放到了下一阶段:
- 网络依赖:实时上传意味着写作过程依赖网络。离线写作是 Gridea Pro 的核心优势之一,我们不想在这个场景引入网络依赖
- 交互复杂度:需要处理上传进度、失败重试、网络异常回退(回退到本地路径),编辑器的复杂度会显著增加
- 先把基础做稳:部署时批量上传是更安全、更可控的方案,先跑通这条路,积累经验后再做编辑器内上传
值得一提的是,路径模板系统的设计已经为这个场景做了预留——它可以为每个文件生成独立的远程路径,正是编辑器内单图上传需要的能力。
八、结语
CDN 图床解决的不是「图片能不能显示」的问题,而是「图片能不能又快又省心地显示」的问题。
仓库体积控制、全球加速访问、自动化上传——这三件事单拿出来都不新鲜,但把它们串成一条无感的链路,让用户只需点一次部署按钮,剩下的全自动完成,这才是我们想做到的。
好的工具应该是透明的——你感受不到它的存在,但它在背后把一切都安排好了。写博客应该专注在内容上,而不是和基础设施搏斗。
从零到一实现这个功能,最大的收获不是写了多少代码,而是在设计过程中反复追问:用户真正需要的是什么?哪些复杂度应该由工具承担,而不是转嫁给用户?
答案往往很简单:能自动的就自动,能省的就省,能不让用户思考的就别让他们思考。
评论