)
1. 本地 Markdown 写作的图片上传痛点与自动化思路写博客最烦的环节往往不是码字而是插图。你在本地 Markdown 里写得好好的一插Typora 预览也正常可一旦要发到 CSDN、掘金、知乎问题就来了平台不认本地路径你得一张张手动上传再把images/arch.png换成https://xxx.com/arch.png。一篇文章十几张图来回切换窗口、复制粘贴半小时就没了还容易漏图、错图。我试过最原始的办法——手动传图床再替换结果一篇文章 23 张图替换到第 15 张时手滑把两张图的链接搞反了发布后读者看到的是错位的截图。后来我意识到这件事本质上是「本地相对路径 → 公网 HTTPS 链接」的批量映射问题完全可以用工具链自动化。这套工作流的核心思路是本地继续用相对路径写 Markdown发布前跑一次批处理把本地图片全部上传到 Cloudflare R2自动回填公开链接生成一份可直接导入平台的发布版 Markdown。三个角色分工明确——Cloudflare R2 当图片仓库PicList 当上传通道Codex 当调度和校验的大脑。为什么选 Cloudflare R2 而不是传统图床R2 兼容 S3 协议PicList 可以直接对接它有公开开发域名图片能直接外链存储成本对个人博客来说几乎可以忽略。为什么用 PicList 而不是自己写上传脚本PicList 内置了本地 Server 接口http://127.0.0.1:36677/upload一个 POST 就能传图省去了自己处理 S3 签名、分片、重试的麻烦。为什么让 Codex 参与因为批量改写 Markdown、跳过代码块里的示例图片、处理 HTMLimg标签、生成映射报告这些逻辑用自然语言描述给 Codex 比写死脚本更灵活。适合谁适合用 Markdown 写博客、图片存在本地、需要多平台分发的开发者。如果你只用 CSDN 在线编辑器直接写那这套流程对你意义不大但只要你本地有.md文件和images/目录这套方案就能把发布前的图片处理压缩成一条命令。整个链路是这样的本地 Markdown 本地图片 → Codex 调用发布脚本 → PicList 上传到 R2 → 脚本替换路径为公开 HTTPS 链接 → 生成发布版 Markdown。下面我从 R2 配置开始一步步拆给你看。2. TaoToken 统一 Key 接入给 Codex 一个稳定的 API 通道在讲 R2 和 PicList 之前得先解决 Codex 的 API 通道问题。Codex 在执行发布任务时需要调用大模型来理解你的指令、生成替换逻辑、校验结果。如果你用的是官方直连可能会遇到网络波动、额度限制、多工具切换 Key 的麻烦。我的做法是用 TaoToken 统一管理 API Key一个 Key 走通模型对话、Coding Plan 和接入文档里的所有能力。TaoToken 是什么简单说它是一个统一的 API 接入层把模型调用、编码计划、控制台管理整合在一起。你不需要在 Codex、Cline、Claude Code 之间来回切换不同的 Key 和 Base URL一个 TaoToken 的 Key 就能覆盖。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。具体到这套工作流Codex 需要的是一个能稳定响应、支持长上下文、能处理批量文本替换的模型通道。TaoToken 的 Coding Plan 适合长期编码和 Agent 场景模型对话适合验证模型是否正常工作API Keys 页面用来生成和管理你的 Key。我建议你先去模型对话页面测一下通道是否通畅确认能正常返回后再接入 Codex。接入方式很简单在 Codex 的配置里把 Base URL 指向 TaoToken 的 API 地址Key 填你在控制台生成的 KeyModel ID 选你需要的模型。如果你用的是 Claude Code 类的工具配置逻辑类似——Base URL、Key、Model ID 三件套填对就行。TaoToken 的接入文档里有详细的参数说明照着填不会错。为什么要在图片上传工作流里强调 API 通道因为 Codex 执行发布任务时不是简单跑一个脚本它要读你的 Markdown、识别哪些是本地图片、哪些是远程链接、哪些在代码块里要跳过、哪些是 HTML 标签然后生成替换后的文件和报告。这个过程需要模型有足够的上下文理解能力。如果 API 通道不稳定跑到一半断了你得重新来一遍。用 TaoToken 统一 Key至少保证通道这一层是稳的。另外TaoToken 的 Coding Plan 对长期编码场景有优化如果你打算把这套工作流固化成日常习惯每天写博客都跑一次那 Coding Plan 比按次调用更划算。控制台里可以看用量和额度API Keys 页面可以随时轮换 Key。这些管理功能对一个长期维护的博客工作流来说很实用。配置好 TaoToken 之后Codex 就有了稳定的模型通道。接下来我们进入 R2 的配置这是图片仓库的搭建环节。3. Cloudflare R2 存储桶与 PicList 可复制配置这一节是整套工作流的地基配置一次后面长期用。我会给出可复制的 JSON 和 TOML 片段你照着填就行。3.1 创建 R2 存储桶和 API Token登录 Cloudflare Dashboard左侧菜单进「存储和数据库」→「R2 对象存储」。首次使用需要开通 R2按控制台提示操作即可。然后点「创建存储桶」名字用英文小写比如blog-assets。建议一个博客用一个 Bucket图片目录由脚本按文章 slug 自动分。进入 Bucket 后在「管理」里选「Create API Token」权限选Object Read Write作用范围限定到刚创建的这个 Bucket不要开放给所有 Bucket。创建成功后会显示三项信息Access Key ID、Secret Access Key、Endpoint URL。Secret Access Key 只显示一次立刻保存别写进 Markdown 或 Git 仓库。Endpoint URL 的格式是https://account_id.r2.cloudflarestorage.com这是给 PicList 上传用的。图片公开访问需要另一个地址——在 Bucket 的「设置」里找到「Public Development URL」启用后会得到类似https://pub-xxxx.r2.dev的地址。这两个地址千万别搞混Endpoint 是上传通道Public URL 是展示通道。3.2 PicList 的 S3 配置片段PicList 里选「Amazon S3」作为图床类型然后填下面这些字段。不同版本字段名可能略有差异但逻辑一致{ picBed: { current: s3, s3: { accessKeyId: 你的_ACCESS_KEY_ID, secretAccessKey: 你的_SECRET_ACCESS_KEY, bucketName: blog-assets, endpoint: https://account_id.r2.cloudflarestorage.com, region: auto, customUrl: https://pub-xxxx.r2.dev, path: articles/{slug}/, fileName: {md5} } } }关键点customUrl必须填 R2 的公开开发域名或你的自定义域名不能填 Endpoint。path里的{slug}是文章标识脚本会在上传前动态设置。fileName用{md5}避免重名也可以用{timestamp}或{random}。如果你用 TOML 管理配置等价写法[picbed.s3] accessKeyId 你的_ACCESS_KEY_ID secretAccessKey 你的_SECRET_ACCESS_KEY bucketName blog-assets endpoint https://account_id.r2.cloudflarestorage.com region auto customUrl https://pub-xxxx.r2.dev path articles/{slug}/ fileName {md5}3.3 开启 PicList 内置 ServerPicList 的本地 Server 是脚本调用的入口。在设置里开启内置 Server默认端口36677上传接口是http://127.0.0.1:36677/upload如果你设了 API Key接口变成http://127.0.0.1:36677/upload?keyYOUR_KEY保持 PicList 开着Server 别关。测试一下在 PicList 里随便传一张图复制返回的链接在无痕浏览器里打开能显示就说明 R2 上传和公开访问都通了。如果打不开优先检查customUrl是不是误填成了 Endpoint或者 R2 的 Public Development URL 没启用。3.4 Codex 的 settings 配置片段Codex 需要知道 TaoToken 的 API 通道和 PicList 的上传接口。在项目根目录建一个settings.json{ api: { baseUrl: https://taotoken.net/api, apiKey: 你的_TAOTOKEN_KEY, modelId: 你的_MODEL_ID }, piclist: { uploadUrl: http://127.0.0.1:36677/upload, apiKey: }, r2: { publicUrlPrefix: https://pub-xxxx.r2.dev, remoteDirTemplate: articles/{slug}/ } }这个文件把三件套——Base URL、Key、Model ID——和 PicList 接口、R2 公开域名都集中管理。Codex 执行任务时读这个文件不用每次在对话里重复参数。注意apiKey不要提交到公开仓库用.gitignore排除。配置完成后你的环境就具备了R2 存储桶能存图PicList 能传图Codex 有稳定的 API 通道。下一节我们验证整条链路。4. 验证请求从粘贴图片到链接回填的完整动作配置对不对跑一次就知道。这一节我带你走一遍完整验证本地 Markdown 里插一张图让 Codex 执行发布看链接是否回填成功。4.1 准备测试文章建一个测试目录my-blog/ └── test-workflow/ ├── article.md └── images/ └── test.pngarticle.md内容# 测试文章 这是一张本地图片  下面这张是远程图片应该被跳过  代码块里的图片语法不应该被处理 python img ### 4.2 让 Codex 执行发布 打开 Codex把项目目录加载进去然后说请执行 Markdown 图片发布工作流输入文件test-workflow/article.mdPicList 上传接口http://127.0.0.1:36677/uploadR2 公开域名https://pub-xxxx.r2.dev远程目录articles/test-workflow/要求上传所有本地图片、替换 Markdown 和 HTML 图片路径、 跳过远程链接和代码块、校验公开链接、输出发布版和映射报告。Codex 会读取 settings.json调用发布脚本把 images/test.png 传给 PicListPicList 上传到 R2 的 articles/test-workflow/ 目录返回公开 URL脚本再把 article.md 里的 images/test.png 替换成 https://pub-xxxx.r2.dev/articles/test-workflow/xxx.png。 ### 4.3 检查输出结果 运行完成后目录里会多出几个文件test-workflow/ ├── article.md ├── articlePicList发布版.md ├── articlePicList发布版.image-map.json ├── articlePicList发布版.publish-report.json └── images/ └── test.png打开 articlePicList发布版.md你应该看到 markdown # 测试文章 这是一张本地图片  下面这张是远程图片应该被跳过  代码块里的图片语法不应该被处理 python img 本地路径被替换了远程链接没动代码块里的示例也没被误伤。再看 publish-report.json json { original_local_image_refs: 1, rewritten_remote_refs: 1, remaining_local_image_refs: 0, skipped_remote_refs: 1, skipped_code_block_refs: 1 }remaining_local_image_refs为 0说明没有遗漏。skipped_remote_refs和skipped_code_block_refs说明跳过逻辑生效了。4.4 验证公开链接复制发布版里的图片链接在无痕浏览器打开。能显示就说明 R2 公开访问正常。如果打不开回到 PicList 检查customUrl配置或者去 Cloudflare 确认 Public Development URL 已启用。这一步验证通过后你就可以把这套流程用到真实文章上了。日常操作就是写完 Markdown确认 PicList 开着让 Codex 跑一次拿发布版去导入平台。5. 常见报错排查401、local proxy failed、reading choices、OAuth跑这套工作流最容易卡在几个典型报错上。我按实际遇到的顺序拆解。5.1 401 Unauthorized这个报错通常出现在两个地方TaoToken API 调用返回 401或者 PicList 上传返回 401。如果是 TaoToken 返回 401检查settings.json里的apiKey是否填对Base URL 是否是https://taotoken.net/api。去 TaoToken 控制台的 API Keys 页面确认 Key 没过期、没被禁用。如果用的是 Coding Plan确认额度没用完。如果是 PicList 返回 401检查 PicList 内置 Server 是否设了 API Key。如果设了上传接口要带?keyYOUR_KEY。另外确认 R2 的 Access Key ID 和 Secret Access Key 没填反Endpoint 没写错。5.2 local proxy failed这个报错说明 Codex 或脚本尝试连接 PicList 的本地接口时失败了。原因通常是 PicList 没开、内置 Server 没启动、端口不是 36677、或者防火墙拦了。排查步骤确认 PicList 进程在运行在 PicList 设置里看内置 Server 是否开启用浏览器访问http://127.0.0.1:36677/upload看是否有响应检查系统防火墙是否允许该端口。如果改过端口把settings.json里的uploadUrl同步改掉。5.3 reading choices 报错这个报错一般出现在模型返回格式不符合预期时。Codex 调用 TaoToken 的模型通道期望返回结构化的替换结果但模型可能返回了自然语言描述。解决办法是在提示词里明确要求输出 JSON 格式或者在settings.json里指定支持结构化输出的 Model ID。如果用的是 Claude Code 类工具检查 Model ID 是否填对。TaoToken 的接入文档里有各模型对应的 ID别填错。另外确认 Base URL 末尾没有多余的斜杠https://taotoken.net/api和https://taotoken.net/api/在某些客户端里行为不同。5.4 OAuth 相关报错如果你在 Codex 或 Claude Code 里配置了 OAuth 登录可能会遇到 token 过期或回调失败。这套工作流建议直接用 API Key 模式不走 OAuth。在settings.json里填apiKeyBase URL 指向 TaoTokenModel ID 选好三件套齐全就不需要 OAuth。如果你之前配过 OAuth先把旧的认证信息清掉避免冲突。Codex 的auth.json里如果残留了旧 token删掉后重新用 API Key 配置。Cline MCP 的配置同理Base URL、Key、Model ID 三件套填对别混用 OAuth 和 API Key。5.5 图片上传成功但链接打不开这不是报错但比报错更隐蔽。PicList 显示上传成功返回了 URL但浏览器打开是 404 或 403。原因通常是customUrl填成了 Endpoint或者 R2 的 Public Development URL 没启用或者 Bucket 的公开访问权限没开。去 Cloudflare R2 的 Bucket 设置里确认 Public Development URL 是启用状态。然后检查 PicList 的customUrl是不是https://pub-xxxx.r2.dev这种格式而不是https://account_id.r2.cloudflarestorage.com。这两个地址长得像但用途完全不同。5.6 部分图片没被替换检查publish-report.json里的remaining_local_image_refs。如果大于 0看image-map.json里哪些图片没被识别。常见原因是 Markdown 写法非常规比如带 title或者 HTML 标签里有复杂属性。把特殊写法改成标准形式重新跑一次。如果图片在代码块里脚本会跳过这是正常行为。如果图片路径是data:开头或//协议相对地址也会跳过。这些都不是本地文件不需要上传。6. 语义一致 CTA把工作流固化成日常习惯这套工作流跑通一次之后日常写博客的流程就变成了本地写 Markdown图片用相对路径插写完让 Codex 跑一次发布拿发布版导入 CSDN。整个过程你只需要关注内容图片上传和链接替换交给工具链。如果你还没配置 TaoToken 的 API 通道建议先去 https://taotoken.net/api-keys 生成一个 Key然后在 https://taotoken.net/doc 看接入文档把 Base URL、Key、Model ID 三件套填进 Codex 或 Claude Code。模型对话页面可以用来验证通道是否正常Coding Plan 适合长期编码和 Agent 场景。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。PicList 和 R2 的配置一次搞定后面不用再动。Codex 的提示词模板可以存成一个文件每次复制粘贴。发布脚本的输出文件建议保留image-map.json和publish-report.json在排查问题时很有用。最后说一个我踩过的坑原始 Markdown 和发布版 Markdown 一定要分开存。原始版保留相对路径方便本地预览和迁移发布版用公开链接专门用于导入平台。别把发布版覆盖原始版否则下次想改文章图片链接全是远程的本地预览就断了。建议命名上区分清楚比如article.md和articlePicList发布版.md一眼就能看出哪个是哪个。