折腾笔记

MD2WX 图床系统与自动换链指南

基于 R2 搭建零成本图床,支持 Web 与 CLI 上传,推稿时自动换链至微信 CDN。

MD2WX 图床系统与自动换链指南

作者:野生宝藏箱
项目:MD2WX(微信公众号 Markdown 排版转换器与草稿箱直推工具)
核心主题:图床系统演进、Cloudflare R2 零成本搭建与微信 CDN 自动换链


MD2WX 图床与 Cloudflare R2 自动化流水线

00. 痛点:为什么写公众号时,配图总在折磨创作者?

在微信公众号写作流程中,文章正文的排版转换往往只需要几秒钟,但配图处理却经常让创作者陷入繁琐的机械操作。

主要痛点集中在三个方面:

1. 外部图片防盗链拦截(破图红叉)

在本地写技术长文或产品笔记时,经常需要引用 GitHub 仓库截图、技术文档图表或外部博客配图。当你将渲染好的富文本直接粘贴到微信公众平台编辑器时,微信后台会严格校验图片来源的 HTTP Referer。绝大多数外部图床或平台都会触发防盗链拦截,文章群发后读者端直接显示为红叉,或显示文字提示:“此图片来自未授权平台,不可引用”

2. 本地图片无法直接复制进微信后台

本地 Markdown 文件中,插图通常采用相对路径写法,例如 ![架构图](./assets/arch.png)。在本地 VS Code 或 Obsidian 里预览没有任何问题,但由于浏览器安全沙箱限制,微信网页版编辑器根本无法读取你本地硬盘上的绝对或相对路径。
以往很多人的妥协做法是:打开微信后台编辑器,点击“上传图片”,从文件管理器中一张张找出对应的配图上传,再人工替换掉正文中的占位符。一篇包含十几张插图的技术文章,仅这一项机械操作就要耗费 20 到 30 分钟。

3. 传统图床工具的配置负担与凭据安全风险

市面上的图床客户端工具(如 PicGo 等)固然强大,但在面向 Web 静态工作台或团队协作时存在天然矛盾:

  • 如果要在纯前端(如 GitHub Pages 托管的 Web Studio)实现上传,通常需要将云厂商对象存储(AWS S3、腾讯云 COS、阿里云 OSS)的 AccessKeySecretKey 填写在前端配置中,这极易导致主凭据泄露;
  • 商业对象存储往往有固定的存储费用与下行流量计费,稍有不慎遭遇恶意盗刷,容易产生意料之外的账单;
  • 对很多仅仅想写写文章的个人创作者来说,配置一整套存储桶权限、CORS 规则和鉴权算法门槛过高。

针对上述问题,MD2WX 在最近的更新中提供了一套完整的图片处理闭环方案。

微信外部图片防盗链拦截(左)与 MD2WX 自动转存对比(右)


01. MD2WX 的三层图片架构

为了彻底解决图片破图与本地上传难题,MD2WX 从底至上设计了三层互为补充的图片处理链路:

[本地 Markdown 编写]

  ├── 1. Web Studio / CLI 手动上传
  │      └──> Cloudflare Worker 代理 (/api/public/uploads 或 /api/uploads)
  │             └──> 写入 Cloudflare R2 存储桶 (10GB 免费,无出流量费)
  │                    └──> 获得公网稳定外链,插入 Markdown

  └── 2. 执行 CLI 一键推稿 (md2wx article.md --publish)
         └──> 解析正文中的全部图片 (本地路径 / 外部 URL / R2 链接)
                └──> 调用微信官方 media/uploadimg 接口
                       └──> 全自动上传并重写为微信官方 CDN (mmbiz.qpic.cn) 永久链接

第一层:微信官方 CDN 自动换链(零配置兜底)

这是 MD2WX 最省心的特性,不需要用户配置任何第三方图床

当你在命令行执行 md2wx article.md --publish 时,内置的 uploader.py 会在提交草稿箱之前自动执行以下动作:

  1. 扫描 AST 中所有的 <img> 标签与 Markdown 图片语法;
  2. 识别本地相对路径文件(如 ./images/demo.png)或外部公网 URL;
  3. 将图片二进制数据读取或下载至内存,计算 MD5 指纹(在本地 ~/.config/md2wx/ 下建立映射缓存,避免重复上传相同图片);
  4. 调用微信公众平台官方的 media/uploadimg 接口,将图片上传至微信官方素材 CDN;
  5. 将正文中所有的原图片链接,逐一就地重写为微信官方的 https://mmbiz.qpic.cn/... 永久链接。

这意味着,你在本地写稿时可以随意使用相对路径,推送到微信后台后,正文里的图片已全部变成了合规且永久免防盗链的微信官方 CDN 地址。

第二层:统一契约的通用图床客户端

如果创作者希望在写作阶段就生成可供公网访问的图片链接(例如在 Web Studio 中实时预览,或将文章同步到其他博客平台),MD2WX 在 CLI 和 Web 端内置了通用的图床客户端(imagehost.pyimagehost.js):

  • 采用通用 multipart/form-data 标准接口契约;
  • 表单字段名(默认 file)、鉴权头(默认 Authorization)、令牌前缀及返回 JSON 提取路径均支持灵活配置;
  • 不仅支持对接自建服务,也能无缝接入各种兼容标准表单上传的图床服务。

第三层:Cloudflare Worker + R2 官方开源模板(零成本持久化)

为了给不想购买昂贵商业存储的用户提供开箱即用的方案,MD2WX 在仓库中直接开源了一个轻量级代理服务模板(位于 cloudflare/r2-imagehost/,代码量仅约 150 行)。

为什么选用 Cloudflare R2?

  • 免费额度充足:每个 Cloudflare 账户享有每月 10GB 的免费存储额度,数百万次标准读写操作;
  • 完全免收出流量费(Zero Egress Fee):与 AWS S3 等产品不同,Cloudflare R2 不对数据传出计费,彻底消除被盗刷流量导致巨额账单的后顾之忧;
  • 全球边缘 CDN 加速:依托 Cloudflare 全球 Anycast 网络,图片访问速度极快。

Cloudflare Worker 双路由隔离、R2 存储与微信 CDN 自动同步架构拓扑


02. 两分钟搭建你的专属 R2 图床

如果你还没有个人图床,可以按照以下步骤使用 MD2WX 提供的模板部署一个。

准备条件

  • 具备 Node.js 环境;
  • 一个已注册的 Cloudflare 免费账户。

部署步骤

进入仓库的 cloudflare/r2-imagehost 目录:

cd cloudflare/r2-imagehost

# 1. 登录 Cloudflare(会唤起浏览器授权)
npx wrangler login

# 2. 创建一个 R2 存储桶(名称需与 wrangler.toml 中的 bucket_name 一致)
npx wrangler r2 bucket create md2wx-images

# 3. 设置私密上传令牌(用于保护 CLI 接口,输入一段随机安全密钥即可)
npx wrangler secret put UPLOAD_TOKEN

# 4. 执行一键部署
npx wrangler deploy

部署完成后,终端会打印出分配给你的 Worker 地址: https://md2wx-imagehost.<你的子域>.workers.dev

接口路由说明

请求方法路径鉴权方式适用场景
POST/api/uploads需携带 Authorization: Bearer <TOKEN>CLI 本地调用或私有脚本
POST/api/public/uploads免密,受 Origin 白名单与 IP 速率限制保护Web 静态站(不泄露主凭据)
GET/images/<key>公开访问,内置 Cache-Control: immutable图片高速分发取图
GET/health公开访问服务探活与连通性检查

你可以通过一条简单的 curl 命令验证图床是否正常运作:

curl -X POST https://md2wx-imagehost.<你的子>.workers.dev/api/uploads \
  -H "Authorization: Bearer <你设定的UPLOAD_TOKEN>" \
  -F "file=@/path/to/test.png"

如果返回如下 JSON,即说明部署成功:

{
  "url": "https://md2wx-imagehost.<你的子域>.workers.dev/images/2026/09/24/xxxxxxxx.png"
}

03. 双端接入与使用工作流

图床服务部署就绪后,接入 MD2WX 仅需几行配置。

1. CLI 命令行接入

在项目根目录的 .env 文件(或系统环境变量)中填入:

# 图床接口地址
IMAGE_HOST_UPLOAD_URL=https://md2wx-imagehost.<你的子域>.workers.dev/api/uploads

# 鉴权令牌与前缀
IMAGE_HOST_TOKEN=your_secret_upload_token
IMAGE_HOST_AUTH_PREFIX=Bearer 

工作流 A:单图快速上传

在终端里遇到需要外链的图片,直接使用 --upload-image 命令:

md2wx --upload-image ./screenshots/demo.png

终端会秒级上传并输出标准 Markdown 图片语法:

[+] 图片上传成功!
    原始路径: ./screenshots/demo.png
    外链地址: https://md2wx-imagehost.<你的子域>.workers.dev/images/2026/09/24/a1b2c3d4.png
    Markdown: ![](https://md2wx-imagehost.<你的子域>.workers.dev/images/2026/09/24/a1b2c3d4.png)

工作流 B:一键发布全自动重写

写完文章后,执行发布命令:

md2wx article.md --publish

无论正文里使用的是本地相对路径(如 assets/diagram.png)还是 R2 图床地址,发布引擎都会在向微信草稿箱推送前,自动完成微信 CDN 转存换链,确保微信后台素材库与正文引用完全一致。

2. Web Studio 网页端使用

对于通过浏览器使用 md2wx.zaneven.com 的用户:

  1. 直接上传与插入:在编辑区域上方工具栏中,点击“插入图片”下拉菜单中的“上传图片”,选择本地文件即可完成上传,光标处会自动插入上传后的 Markdown 链接;
  2. 免密安全防护:静态网页端请求的是 /api/public/uploads 接口,前端代码中没有任何私密密钥,仅通过 Origin 白名单校验来源;
  3. 未配置环境的主动提示:如果前端未检测到有效的图床环境变量,点击上传时会弹出温和的指引提示,避免用户误操作。

MD2WX CLI 单图秒传与 Web Studio 拖拽插入实时渲染工作流


04. 架构中的安全防刷与可靠性细节

作为一项面向生产环境的基础功能,MD2WX 在图床设计上兼顾了安全与防护:

1. 严格的凭据隔离

  • R2 的 S3 AccessKey 与 SecretKey 仅存在于 Cloudflare Worker 内部,浏览器前端和普通客户端完全接触不到对象存储的主控制凭据;
  • CLI 端使用独立的 UPLOAD_TOKEN 进行鉴权;
  • Web 端采用来源白名单限制(ALLOWED_ORIGINS),杜绝接口被其它第三方站点肆意调用。

2. Workers Rate Limiting 边缘限流

针对公开路由 /api/public/uploads,模板在 wrangler.toml 中预置了 Cloudflare Workers 原生 Rate Limiting 绑定:

  • 限制单个客户端 IP 每 60 秒最多请求 60 次
  • 正常的文章编写上传(单篇通常几张到几十张)完全不受限制,而恶意的脚本刷量则会在边缘节点直接被拦截并返回 HTTP 429 Too Many Requests,避免存储桶被异常塞满。

3. 本地 MD5 缓存去重

在向微信草稿箱同步图片时,uploader.py 会基于文件内容的 MD5 值在本地维护一份转换索引。如果同一篇文章多次预览或更新草稿,已上传过的图片会直接命中缓存,无需重复消耗网络带宽与微信素材库配额。


05. 总结

优质的内容输出应当专注于构思与文字本身,而不应被破图、防盗链拦截以及人工复制上传所消耗。

通过微信官方 CDN 自动重写通用图床客户端基于 Cloudflare R2 的零成本部署模板,MD2WX 将公众号长文的配图流转环节压缩到了近乎无感的状态。

如果你经常受制于微信排版中的配图痛点,不妨拉取最新版本体验。


项目开源主页https://github.com/zaneven/MD2WX
在线工作台https://md2wx.zaneven.com
图床 Worker 模板cloudflare/r2-imagehost