文章目录 痛点每次发博客图片都是体力活 我想要什么样的传图方式 方案image-upload skill 的架构 安装 使用三个真实场景场景一单张上传upload 原语场景二整篇博客迁移migrate——真正省事的命令场景三切换到 GitHub 后端 几个关键的设计决定✅ 效果与小结发博客最折腾的从来不是写文字而是弄图片——逐张传图床、手动把![[图]]一处处改成图床 URL、漏改错改、重传一遍就在图床堆出一堆重复文件。这篇文章记录我怎么用一个 Claude Code Skillimage-upload把扫描本地图片引用 → 按内容哈希去重上传 → 原地改写链接这条链路压成一句话的事配置自包含不再绑死 PicGo 图形界面重跑幂等不重复传七牛为主、GitHub 为备。 痛点每次发博客图片都是体力活我用 Obsidian 写博客再通过 Hugo 发布。文章里插图很自然地写成 Obsidian 的本地引用![[scheme-writer-architecture.png]] ![[scheme-writer-architecture.png|架构图]]但 Hugo 不认![[...]]这种 wiki 链接它要的是标准 Markdown 的 URL于是每发一篇文章都要重复一套体力活把assets/blog/里的截图逐张上传到图床七牛云拿到 URL手动把文章里的![[图]]一处处改成漏改、改错、重名覆盖是常态重新传一次记不清哪张传过只能再传一遍——图床里堆满重复文件。以前这套流程靠 PicGo 的图形界面点来点去后来我干脆让 AI 每次帮我传。但这还是「每次都要叮嘱一遍」的临时活儿没有沉淀成能力。而且配置散落在 PicGo 里万一哪天 PicGo 不装了连密钥都找不回来。 Obsidian 里一篇带![[...]]本地图片引用的博文草稿截图。 我想要什么样的传图方式需求其实很朴素一条命令搞定别让我一张张点、一处处改AI 能直接调在 Claude Code 里说一句把这篇的图传一下就完事七牛为主、GitHub 为备七牛国内快GitHub 免费可回溯配置不依赖 PicGoskill 要自洽PicGo 装不装都能跑别重复传同一张图传过就别再传URL 还能找回。顺着这几个需求我写了一个 Claude Code Skillimage-upload。这篇文章就讲它解决什么问题、怎么装、怎么用。 方案image-upload skill 的架构先看全貌整个 skill 是分层的自上而下五层层模块职责入口main.pyCLI 派发upload/migrate/init/list编排rewriter.py/manifest.py/keys.py/config.py解析改写、去重账本、key 命名、配置加载抽象Uploader基类对外只暴露一个接口upload(local_path, key) → URL实现QiniuUploader/GithubUploader七牛 SDK / GitHub Contents API图床七牛云 / GitHub 仓库实际存储最关键的设计是「Provider 抽象」上层migrate命令只认upload()这个接口根本不在乎底下是七牛还是 GitHub。所以多一个图床后端只是多写一个Uploader子类编排逻辑一行不改。这就是后面能轻松加上 GitHub 的原因。配置全部在 skill 自己的.env里自包含config.py加载并校验。PicGo 只在首次init时当一次密钥搬运工搬完即可卸载skill 此后完全独立。 安装# 1. 拿到 skill仓库里或同步到 ~/.claude/skills/image-upload/cdimage-upload# 2. 建虚拟环境、装依赖含测试是 requirements-dev.txtpython3-mvenv .venv .venv/bin/pipinstall-rrequirements.txt# 3. 配置二选一cp.env.example .env$EDITOR.env# 手填# 或者——如果你机器上还有 PicGo一行导入七牛配置.venv/bin/python main.py initinit会读 PicGo 的data.json把里面的picBed.qiniuaccessKey/secretKey/bucket/domain/area一次性写进.env[done] 已从 PicGo 导入七牛配置到 .../image-upload/.env bucketishareblog, domainhttp://image2.ishareread.com, areaz2 PicGo 此后可卸载skill 已自包含。 使用三个真实场景场景一单张上传upload原语最原始的能力——给我一个图片文件还你一个图床 URL.venv/bin/python main.py upload assets/blog/foo.png--json[{file:/abs/path/foo.png,url:http://image2.ishareread.com/images/20260626/foo.png,status:uploaded,backend:qiniu}]--json输出结构化结果方便脚本/AI 组合调用不加就是一行本地路径 - URL。这个原语的价值在于可组合任何需要文件→URL的流程都能调它不限于博客。上传成功后直接返回可以用的外链URL场景二整篇博客迁移migrate——真正省事的命令这才是解决体力活的核心。给一篇文章它自动扫描所有本地图片引用、逐张上传、原地改写链接。先--dry-run预览不传不改.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--dry-run --vault-root ~/my-knowledge-base[dry-run] 将上传 scheme-writer-architecture.png - qiniu:images/scheme-writer-guide/scheme-writer-architecture.png [dry-run] 将上传 scheme-writer-workflow.png - qiniu:images/scheme-writer-guide/scheme-writer-workflow.png [dry-run] 计划上传 2manifest命中 0已是URL跳过 1缺失 0未改写文件确认无误去掉--dry-run真跑.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--vault-root ~/my-knowledge-base[done] 改写 2 处上传 2manifest命中 0已是URL跳过 1缺失 0文章里的链接原地从这样![[scheme-writer-architecture.png]] ![[scheme-writer-architecture.png|架构图]] 变成了这样  注意三件事都对了![[x.png]]→无 alt![[x.png|架构图]]→alt 文本保留了已经是 URL 的那行原样不动——不会重复上传。整个migrate的工作流场景三切换到 GitHub 后端同一篇文章想改用 GitHub 当图床加个--backend github.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--backendgithub --vault-root ~/my-knowledge-base[done] 改写 2 处上传 2manifest命中 0已是URL跳过 1缺失 0返回的 URL 变成https://raw.githubusercontent.com/owner/repo/main/images/...。而且——七牛那次的记录还在同一张图同时挂着两个后端的 URL随时能切回来。为什么 GitHub 默认走raw.githubusercontent.com因为博客默认后端仍是七牛国内快GitHub 是免费/可回溯/给非国内读者的备用。真要 GitHub 图也跑国内流量把.env里的GH_DOMAIN指向一个 Cloudflare Worker 反代域名就行零代码改动。 几个关键的设计决定1. 内容哈希去重而不是按文件名manifest 按文件的sha256 内容哈希做索引不是按文件名或路径。同一张截图在多处引用 → 只传一次省图床空间图片被重新编辑过内容变了→ 自动识别为新文件重传新版本文件改名/移动 → 不影响哈希一样还是命中。这是重跑不重复传、URL 可回溯的基础。manifest 同时分桶记七牛和 GitHub 两条 URL所以后端切换不重传。2. 配置自包含不绑死 PicGo.env存所有参数七牛 GitHub。PicGo 仅在init时当一次密钥搬运工。这是有意的我正在逐步脱离 PicGo 的图形界面skill 不能因为 PicGo 不在就瘫掉。改密钥在.env改就行PicGo 不再是必经之路。3. 链接改写按 offset不用字符串替换文章里同一张图引用多次时朴素的str.replace会误伤。skill 记录每个引用在原文的精确偏移从后往前逐个替换重复引用也不会错位。4. 文件名自动净化URL 永远安全Obsidian 粘贴的截图默认叫Pasted image 20260626094343.png这种带空格的名字直接当 key 会让图床 URL 也带空格、贴进 Markdown 就断链。skill 在生成 key 时对文件名做净化空格变-、去掉()/#/?/等 URL 不安全字符、折叠多余连字符中文保留。于是Pasted image x.png→Pasted-image-x.png图 1.png→图-1.png——命名再随意也不会产出断链 URL。5. YAGNI没做的事不做图形界面Claude Code 就是界面不做图片压缩/格式转换需要时再加--optimize不做多用户鉴权本地个人 skill。✅ 效果与小结博客写完后截图都是本地引用以前需要一张张图片上传然后手动改链接。这套东西落地后发博客的图片流程从逐张上传 手动改链接压缩成了一条命令在Obsidian中对着Claudin说请将‘告别手动传图-用image-upload-Skill把博客图片托管自动化’博文中的图片上传到图床并更新链接写完文章直接跑链接自动改写好Hugo 那边刷新就能看到图文并茂的成品。重跑安全幂等后端可切七牛/GitHub配置自洽不依赖 PicGo。[截图]发布到 Hugo 后浏览器里渲染出的图文博客效果展示一篇有有截图的完整文章。一个有点元的小事你正在看的这篇文章里的两张架构图上面的架构图和 migrate 流程图就是用这个 skill 自己传到图床的——upload /tmp/arch.png /tmp/flow.png一行搞定URL 拿来直接贴。自己造的轮子自己先用起来。如果你也在用 Obsidian Hugo 写博客、又被图片托管折腾过可以照着这个思路做一个。完整的代码和设计文档我都放在了 myopenclaw-skills 仓库的image-upload/目录下载后就可以直接用设计文档在docs/plans/2026-06-25-image-upload-skill-design.md欢迎自取。作者博客http://xiejava.ishareread.com/
告别手动传图-用image-upload-Skill把博客图片托管自动化
发布时间:2026/6/27 17:29:03
文章目录 痛点每次发博客图片都是体力活 我想要什么样的传图方式 方案image-upload skill 的架构 安装 使用三个真实场景场景一单张上传upload 原语场景二整篇博客迁移migrate——真正省事的命令场景三切换到 GitHub 后端 几个关键的设计决定✅ 效果与小结发博客最折腾的从来不是写文字而是弄图片——逐张传图床、手动把![[图]]一处处改成图床 URL、漏改错改、重传一遍就在图床堆出一堆重复文件。这篇文章记录我怎么用一个 Claude Code Skillimage-upload把扫描本地图片引用 → 按内容哈希去重上传 → 原地改写链接这条链路压成一句话的事配置自包含不再绑死 PicGo 图形界面重跑幂等不重复传七牛为主、GitHub 为备。 痛点每次发博客图片都是体力活我用 Obsidian 写博客再通过 Hugo 发布。文章里插图很自然地写成 Obsidian 的本地引用![[scheme-writer-architecture.png]] ![[scheme-writer-architecture.png|架构图]]但 Hugo 不认![[...]]这种 wiki 链接它要的是标准 Markdown 的 URL于是每发一篇文章都要重复一套体力活把assets/blog/里的截图逐张上传到图床七牛云拿到 URL手动把文章里的![[图]]一处处改成漏改、改错、重名覆盖是常态重新传一次记不清哪张传过只能再传一遍——图床里堆满重复文件。以前这套流程靠 PicGo 的图形界面点来点去后来我干脆让 AI 每次帮我传。但这还是「每次都要叮嘱一遍」的临时活儿没有沉淀成能力。而且配置散落在 PicGo 里万一哪天 PicGo 不装了连密钥都找不回来。 Obsidian 里一篇带![[...]]本地图片引用的博文草稿截图。 我想要什么样的传图方式需求其实很朴素一条命令搞定别让我一张张点、一处处改AI 能直接调在 Claude Code 里说一句把这篇的图传一下就完事七牛为主、GitHub 为备七牛国内快GitHub 免费可回溯配置不依赖 PicGoskill 要自洽PicGo 装不装都能跑别重复传同一张图传过就别再传URL 还能找回。顺着这几个需求我写了一个 Claude Code Skillimage-upload。这篇文章就讲它解决什么问题、怎么装、怎么用。 方案image-upload skill 的架构先看全貌整个 skill 是分层的自上而下五层层模块职责入口main.pyCLI 派发upload/migrate/init/list编排rewriter.py/manifest.py/keys.py/config.py解析改写、去重账本、key 命名、配置加载抽象Uploader基类对外只暴露一个接口upload(local_path, key) → URL实现QiniuUploader/GithubUploader七牛 SDK / GitHub Contents API图床七牛云 / GitHub 仓库实际存储最关键的设计是「Provider 抽象」上层migrate命令只认upload()这个接口根本不在乎底下是七牛还是 GitHub。所以多一个图床后端只是多写一个Uploader子类编排逻辑一行不改。这就是后面能轻松加上 GitHub 的原因。配置全部在 skill 自己的.env里自包含config.py加载并校验。PicGo 只在首次init时当一次密钥搬运工搬完即可卸载skill 此后完全独立。 安装# 1. 拿到 skill仓库里或同步到 ~/.claude/skills/image-upload/cdimage-upload# 2. 建虚拟环境、装依赖含测试是 requirements-dev.txtpython3-mvenv .venv .venv/bin/pipinstall-rrequirements.txt# 3. 配置二选一cp.env.example .env$EDITOR.env# 手填# 或者——如果你机器上还有 PicGo一行导入七牛配置.venv/bin/python main.py initinit会读 PicGo 的data.json把里面的picBed.qiniuaccessKey/secretKey/bucket/domain/area一次性写进.env[done] 已从 PicGo 导入七牛配置到 .../image-upload/.env bucketishareblog, domainhttp://image2.ishareread.com, areaz2 PicGo 此后可卸载skill 已自包含。 使用三个真实场景场景一单张上传upload原语最原始的能力——给我一个图片文件还你一个图床 URL.venv/bin/python main.py upload assets/blog/foo.png--json[{file:/abs/path/foo.png,url:http://image2.ishareread.com/images/20260626/foo.png,status:uploaded,backend:qiniu}]--json输出结构化结果方便脚本/AI 组合调用不加就是一行本地路径 - URL。这个原语的价值在于可组合任何需要文件→URL的流程都能调它不限于博客。上传成功后直接返回可以用的外链URL场景二整篇博客迁移migrate——真正省事的命令这才是解决体力活的核心。给一篇文章它自动扫描所有本地图片引用、逐张上传、原地改写链接。先--dry-run预览不传不改.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--dry-run --vault-root ~/my-knowledge-base[dry-run] 将上传 scheme-writer-architecture.png - qiniu:images/scheme-writer-guide/scheme-writer-architecture.png [dry-run] 将上传 scheme-writer-workflow.png - qiniu:images/scheme-writer-guide/scheme-writer-workflow.png [dry-run] 计划上传 2manifest命中 0已是URL跳过 1缺失 0未改写文件确认无误去掉--dry-run真跑.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--vault-root ~/my-knowledge-base[done] 改写 2 处上传 2manifest命中 0已是URL跳过 1缺失 0文章里的链接原地从这样![[scheme-writer-architecture.png]] ![[scheme-writer-architecture.png|架构图]] 变成了这样  注意三件事都对了![[x.png]]→无 alt![[x.png|架构图]]→alt 文本保留了已经是 URL 的那行原样不动——不会重复上传。整个migrate的工作流场景三切换到 GitHub 后端同一篇文章想改用 GitHub 当图床加个--backend github.venv/bin/python main.py migrate notes/blog/posts/scheme-writer-guide.md\--backendgithub --vault-root ~/my-knowledge-base[done] 改写 2 处上传 2manifest命中 0已是URL跳过 1缺失 0返回的 URL 变成https://raw.githubusercontent.com/owner/repo/main/images/...。而且——七牛那次的记录还在同一张图同时挂着两个后端的 URL随时能切回来。为什么 GitHub 默认走raw.githubusercontent.com因为博客默认后端仍是七牛国内快GitHub 是免费/可回溯/给非国内读者的备用。真要 GitHub 图也跑国内流量把.env里的GH_DOMAIN指向一个 Cloudflare Worker 反代域名就行零代码改动。 几个关键的设计决定1. 内容哈希去重而不是按文件名manifest 按文件的sha256 内容哈希做索引不是按文件名或路径。同一张截图在多处引用 → 只传一次省图床空间图片被重新编辑过内容变了→ 自动识别为新文件重传新版本文件改名/移动 → 不影响哈希一样还是命中。这是重跑不重复传、URL 可回溯的基础。manifest 同时分桶记七牛和 GitHub 两条 URL所以后端切换不重传。2. 配置自包含不绑死 PicGo.env存所有参数七牛 GitHub。PicGo 仅在init时当一次密钥搬运工。这是有意的我正在逐步脱离 PicGo 的图形界面skill 不能因为 PicGo 不在就瘫掉。改密钥在.env改就行PicGo 不再是必经之路。3. 链接改写按 offset不用字符串替换文章里同一张图引用多次时朴素的str.replace会误伤。skill 记录每个引用在原文的精确偏移从后往前逐个替换重复引用也不会错位。4. 文件名自动净化URL 永远安全Obsidian 粘贴的截图默认叫Pasted image 20260626094343.png这种带空格的名字直接当 key 会让图床 URL 也带空格、贴进 Markdown 就断链。skill 在生成 key 时对文件名做净化空格变-、去掉()/#/?/等 URL 不安全字符、折叠多余连字符中文保留。于是Pasted image x.png→Pasted-image-x.png图 1.png→图-1.png——命名再随意也不会产出断链 URL。5. YAGNI没做的事不做图形界面Claude Code 就是界面不做图片压缩/格式转换需要时再加--optimize不做多用户鉴权本地个人 skill。✅ 效果与小结博客写完后截图都是本地引用以前需要一张张图片上传然后手动改链接。这套东西落地后发博客的图片流程从逐张上传 手动改链接压缩成了一条命令在Obsidian中对着Claudin说请将‘告别手动传图-用image-upload-Skill把博客图片托管自动化’博文中的图片上传到图床并更新链接写完文章直接跑链接自动改写好Hugo 那边刷新就能看到图文并茂的成品。重跑安全幂等后端可切七牛/GitHub配置自洽不依赖 PicGo。[截图]发布到 Hugo 后浏览器里渲染出的图文博客效果展示一篇有有截图的完整文章。一个有点元的小事你正在看的这篇文章里的两张架构图上面的架构图和 migrate 流程图就是用这个 skill 自己传到图床的——upload /tmp/arch.png /tmp/flow.png一行搞定URL 拿来直接贴。自己造的轮子自己先用起来。如果你也在用 Obsidian Hugo 写博客、又被图片托管折腾过可以照着这个思路做一个。完整的代码和设计文档我都放在了 myopenclaw-skills 仓库的image-upload/目录下载后就可以直接用设计文档在docs/plans/2026-06-25-image-upload-skill-design.md欢迎自取。作者博客http://xiejava.ishareread.com/