1. 先说清楚:ImgNaondo 到底解决了什么问题?
经常写教程、整理文档的人,很容易遇到这样一连串的小麻烦:
- 本地截图太多,散落在各个文件夹;
- 贴到文章里要先上传第三方图床,再复制 URL;
- 某天第三方图床抽风、限流或者关站,整篇文章插图全军覆没。
ImgNaondo 的目标很直接:在 Cloudflare Workers + R2 上,搭一套只为自己服务的图床,界面简单,使用门槛低,又不需要维护传统服务器。
项目仓库本体在 GitHub 上,可以随时查看源码和更新:
ImgNaondo 仓库 :github.com/xdanielf/Im...

2. 环境准备:需要哪些账号和资源?
整套方案依赖 Cloudflare 的无服务器产品组合:
- Cloudflare 账户(用来创建 Worker 和 R2 存储桶)
- 一个 R2 Bucket,用来存所有图片;
- 一个 Worker 脚本,负责接收上传请求、从 R2 读取图片并输出;
- (可选)一个自定义域名,比如
img.example.com。
如果还没有 Cloudflare 账户,可以先注册一个,后面所有操作都会在同一控制台完成:
Cloudflare 控制台 :dash.cloudflare.com/
注册完成后,只要开启 R2(按量计费,小规模个人使用时成本通常比较友好),就可以继续往下走。 实际上,10GB以内的存储是免费的,单纯存图片很难用得完。
3. 创建 R2 存储桶:给图片找一个"仓库"
在控制台中打开 R2,创建一个新的 Bucket。ImgNaondo 默认约定的名字叫:
text
imgnaondo
当然,也可以用别的名字,只要后面在 Worker 绑定时对应起来就行。
在 R2 这一步,不需要设置复杂的生命周期策略,只要保证:
- Bucket 可以被绑定到 Worker;
- 默认私有,不对公网直接暴露(真正的出入口由 Worker 负责)。
这样做的好处是:以后想换一套管理界面,或者自己写新的前端,都可以继续复用同一个 Bucket。
4. 部署 Worker:让脚本站在 Cloudflare 边缘节点上
接下来是这套图床的"核心发动机"------Cloudflare Worker。
在控制台中新建一个 Worker,把默认示例代码全部删掉,替换成 ImgNaondo 仓库里的 worker.js 内容。为了便于理解,可以先看一个高度简化的版本,大致结构类似这样:
js
export default {
async fetch(request, env) {
const url = new URL(request.url);
// 简单的上传鉴权
if (request.method === "POST") {
const token = request.headers.get("x-upload-token");
if (token !== env.PASSWORD) {
return new Response("Unauthorized", { status: 401 });
}
// 这里处理上传逻辑:读取表单,写入 R2
}
// 读取图片
if (request.method === "GET") {
const key = url.pathname.replace(/^\/+/, ""); // 去掉前导 /
const object = await env.IMAGES.get(key);
if (!object) return new Response("Not found", { status: 404 });
return new Response(object.body, {
headers: {
"Content-Type": object.httpMetadata?.contentType || "image/jpeg",
"Cache-Control": "public, max-age=31536000, immutable"
}
});
}
return new Response("Method Not Allowed", { status: 405 });
}
}
这段伪代码表达出的思路非常清晰:
- 所有上传都必须带一个密码(
PASSWORD环境变量); - 所有读取都通过
env.IMAGES.get(key)从 R2 拿文件; - 返回时加上合理的缓存头,让图片可以在 Cloudflare 边缘节点长时间缓存。
在 Worker 的"Settings → Variables"中,需要设置两个关键内容:
- 一个名为
IMAGES的 R2 Binding,指向刚才创建的imgnaondoBucket; - 一个名为
PASSWORD的环境变量,设置为自定义的上传密码。
配置完成后,点一下"Save and deploy",后端接口就已经上线了。
5. 绑定前端面板:从脚本变成真正可用的图床
后端接口准备好之后,还差一个"人类友好"的前端面板。ImgNaondo 项目自带的那套界面,就对应读者看到的截图:
- 顶部是深色导航栏和 Logout 按钮;
- 中间是巨大的拖拽区域,可以直接拖图片进来;
- 下方有 Custom Name 和 Tags 的输入框;
- 再往下是 Tag Cloud、搜索栏、排序方式和 Bulk Select;
- 最底部则是图片列表(刚搭好时会显示 "No images found.")。
通常的做法,是把前端构建好的静态文件直接塞进 Worker 中,通过 HTML 响应一起返回。一个极简路由示例如下:
js
if (url.pathname === "/" && request.method === "GET") {
return new Response(await env.ASSETS.get("index.html"), {
headers: { "Content-Type": "text/html; charset=utf-8" }
});
}
在实际项目里,HTML、CSS、JS 可能通过 Wrangler 或 Pages Functions 来托管,关键点是:前端只需要知道一个上传接口和一个获取列表的接口,剩下的都交给 Worker + R2 处理。
真正解析时只需要把 img.example.com 的流量指向这个 Worker 即可。
6. 实战操作:从拖拽上传,到在 Markdown 中插图
当后台面板已经可以正常打开时,一次完整的"上传 --- 获取 URL --- 写文档"的流程会非常顺滑。
可以想象这样一个画面:一台笔记本电脑放在桌面上,屏幕里打开的是教程草稿,旁边是浏览器里的 ImgNaondo 后台。读者只要把截图从图片管理器拖到浏览器中间那块虚线框,就能看到上传进度和完成后的缩略图。
示意图可以用一张类似的工作场景照片来辅助理解:

上传完成后,后台会给出一个完整的访问链接,例如:
text
https://img.example.com/2025/11/15/terminal-latency-test.png
在 Markdown 文档里,只要这样插入即可:
markdown

对于经常写教程、做笔记的人来说,这种"看到就能直接复制"的体验,比一堆命令行工具要直观得多。
如果文章托管在 GitHub Pages 或其他静态站点上,也可以继续使用这种方式插图,例如 GitHub 官方的介绍页面:
GitHub Pages 说明 :pages.github.com/
不论文章部署到哪里,只要图床域名不变、R2 不清理,对外可见的图片就会稳定存在。
7. 小小的安全与管理细节
虽然 ImgNaondo 主要面向个人或小团队使用,但一些基础的安全和管理细节还是值得顺手做好。
最关键的是上传密码 PASSWORD。为了避免脚本被别人拿到就随意上传,可以再加一层简单的约束,例如:
js
const allowedOrigins = ["https://img.example.com", "https://wiki.example.com"];
if (request.method === "POST") {
const origin = request.headers.get("Origin");
if (!allowedOrigins.includes(origin)) {
return new Response("Forbidden", { status: 403 });
}
// 再继续密码校验和上传逻辑
}
另外,如果图片数量会长期增长,建议时不时查看一下 R2 的容量和计费情况;必要时还可以设置生命周期规则,将非常古老、几乎不会再访问的图片迁移到更便宜的存储等级。
ImgNaondo 的 Tag Cloud 和搜索功能,也可以帮助管理:给每一批图片打上带项目名或主题的标签,例如 blog, doc, invoice,后期查找和批量整理会轻松很多。