Obsidian 七牛云图片上传插件修复与扩展全记录
有了AI的辅助真的爽,经过这次折腾,补上了附件的七牛云问题,再也不用担心电脑出问题存电脑附件丢失了,同时兼容了所有的Obsidian插件,比如发布到typecho,不再去研究TY的插件,咱直接从OB发布,Markdown格式同步到TY,完美。
这次修复其实蛮有难度的,花了我3小时左右,花这么多时间主要是因为AI以及我自己懒了,过于详细AI能力,没有提供思路给它,AI的脑子还是不够聪明的,比如让它新增边栏图片上传时间显示,它硬是要去折腾传到七牛云的返回时间,其实最简单的是上传文件后的时间戳,来回折腾几十遍它都无法搞定,我告诉它把时间戳格式化输出,几十秒解决问题。
这次的插件整合了之前折腾的TY插件的文件上传功能,实现了不错的边栏,复制删除插入等全部实现,全程有提醒日志。

以下内容是我让AI总结的本次折腾,完整的记录了折腾过程、结果,这样的文件真的很有帮助,这就是AI的魅力哈。
一、原有插件概述
1.1 插件基本信息
- 插件名称:obsidian-qiniu-image-uploader
- 原仓库:https://github.com/tuantu00/obsidian-qiniu-image-uploader
- 版本:v1.1.5(两年前更新的插件,没有多少人下载,问题多多)
- 功能:将 Obsidian 中的图片粘贴上传到七牛云存储,并自动插入 Markdown 链接
1.2 原有特点
- 复制粘贴上传:通过 Ctrl+V 粘贴图片自动上传
- 拖拽上传:从文件管理器拖拽图片到编辑器自动上传
- 七牛云 SDK 支持:使用 qiniu 官方 SDK 进行上传
- 多区域支持:支持 z0、z1、z2、na0、as0 等七牛云区域
- HTTPS/HTTP 切换:支持协议选择
- 自定义文件名前缀:可配置上传文件名的前缀
- 时间格式配置:支持自定义文件名中的时间格式
1.3 原有问题
- 构建失败:
proxy-agent依赖导致 esbuild 打包失败 - 仅支持单张上传:同时粘贴多张图片只能成功上传第一张
- 文件名冲突:多张图片使用相同时间戳,后上传的覆盖前面的
- 依赖过旧:部分依赖包版本过旧,存在安全警告
- npm 源问题:默认使用内网镜像源导致无法下载依赖
二、折腾过程
2.1 环境准备
问题1:npm 源配置错误
现象:
npm error network request to https://registry.antgroup-inc.cn/date-fns/-/date-fns-3.6.0.tgz failed
原因:npm 使用了蚂蚁集团内网镜像源
解决方案:
npm config set registry https://registry.npmmirror.com/
问题2:proxy-agent 打包错误
现象:
X [ERROR] Could not resolve "proxy-agent"
node_modules/urllib/lib/detect_proxy_agent.js:24:51: ERROR: Could not resolve "proxy-agent"
原因:proxy-agent 是 Node.js 原生模块,在 Obsidian 的 Electron 环境中无法打包
解决方案:在 esbuild.config.mjs 的 external 数组中添加 "proxy-agent"
问题3:date-fns 模块找不到
现象:Plugin failure: qiniu-image-uploader Error: Cannot find module 'date-fns'
原因:date-fns 被标记为外部依赖,但 Obsidian 环境中找不到
解决方案:将 date-fns 打包进 main.js(从 external 中移除)
问题4:多张图片只能上传一张
现象:同时粘贴多张图片,只有第一张成功上传
原因:
- 事件回调只处理了
files[0] - 文件名生成使用相同时间戳,后上传覆盖前面的
解决方案:
- 修改事件回调,使用
Array.from处理所有文件 - 新增
uploadMultipleFiles方法批量处理 - 文件名添加索引和随机数确保唯一
关键代码改动:
原代码只处理第一个文件,改为遍历所有文件逐个上传
问题5:qiniu SDK 和 fetch 来回折腾
过程:
- 最初使用
qiniuSDK →proxy-agent打包失败 - 改用 fetch 方式 → 401 认证失败(token 生成算法有 bug)
- 重新安装
qiniuSDK →proxy-agent再次失败 - 最终放弃
qiniuSDK,使用 fetch + 手动生成 token
经验教训:不要来回切换方案,应该先分析根本原因再选择方案
问题6:边栏文件列表加载失败
现象:边栏可以打开但文件列表为空,控制台无错误
原因:使用 fetch 直接调用七牛云 API 被 CORS 策略拦截
解决方案:使用 qiniu SDK 的 bucketManager.listPrefix 方法(内部使用 Node.js http 模块,不受 CORS 限制)
问题7:边栏插入功能失效
现象:点击插入按钮提示"请先打开一个 Markdown 文件"
原因:getActiveEditor 方法无法正确获取编辑器实例
解决方案:增强 getActiveEditor 方法,使用多种方式尝试获取编辑器实例
问题8:文件列表显示全部文件
现象:边栏列出了存储桶中所有文件,包括非本插件上传的
原因:没有使用文件名前缀过滤
解决方案:在 listPrefix 请求中使用 prefix 参数,设置为 文件夹 + namePrefix
问题9:上传时间显示异常
现象:显示为"未知时间"或"1970-01-21"
原因:七牛云返回的 putTime 是纳秒格式,直接使用导致日期错误
最终解决方案:放弃使用 putTime,直接从文件名中提取时间戳(文件名格式为 ob-时间戳-原始文件名)
三、扩展功能
3.1 批量上传支持
功能:支持同时粘贴多张图片,全部自动上传
实现方式:
- 修改
customPasteEventCallback处理所有文件 - 新增
uploadMultipleFiles方法循环上传 - 上传过程中显示进度文本
![Uploading 3 images... xxx]() - 上传完成后批量插入图片链接
效果:
- 从文件夹选中 5 张图片 → Ctrl+C 复制 → 在编辑器中 Ctrl+V 粘贴
- 所有图片依次上传,最终生成 5 个图片链接
3.2 文件名优化
功能:保留原始文件名,便于识别
格式:ob-时间戳-原始文件名
示例:
- 原始文件名:
screenshot-2024.png - 上传后文件名:
ob-1782431380650-screenshot-2024.png
优势:
- 时间戳保证唯一性,避免覆盖
- 原始文件名便于识别和管理
3.3 右侧边栏管理
功能:新增右侧边栏,提供文件管理功能
包含功能:
- 文件列表:显示本插件上传的所有图片
- 上传按钮:点击选择文件上传(支持多选)
- 刷新按钮:刷新文件列表
- 复制链接:一键复制图片 URL
- 插入编辑器:将图片插入到当前光标位置
- 删除文件:从七牛云删除文件
- 分页支持:每页 10/15/20/30/50 条可调
- 缩略图预览:图片类型文件显示缩略图
- 排序:按上传时间倒序排列
操作流程:
- 点击 Obsidian 左侧边栏的"七牛云管理"图标
- 右侧打开文件管理面板
- 可查看、上传、复制、插入、删除文件
3.4 上传时间显示
功能:在文件列表中显示上传时间
显示逻辑:
- < 1 分钟:刚刚
- < 1 小时:X 分钟前
- < 24 小时:X 小时前
- ≥ 24 小时:YYYY-MM-DD
实现方式:从文件名中提取时间戳,计算与当前时间的差值
四、技术难点与解决方案总结
4.1 难点:proxy-agent 打包问题
原因:qiniu SDK 依赖 urllib,urllib 依赖 proxy-agent,后者是 Node.js 原生模块
解决方案:将 proxy-agent 标记为外部依赖
external: ["obsidian", "electron", ...builtins, "proxy-agent"]
4.2 难点:七牛云 token 生成
原因:手动生成 token 需要正确的 HMAC-SHA1 签名和 URL-safe Base64 编码
解决方案:使用 qiniu SDK 的 generateAccessToken 方法生成管理凭证
4.3 难点:CORS 跨域限制
原因:浏览器禁止从 app:// 协议向 https://rs.qbox.me 发起请求
解决方案:使用 qiniu SDK 的 bucketManager,内部使用 Node.js http 模块
4.4 难点:边栏与主插件通信
原因:边栏和主插件是独立的模块,无法直接共享数据
解决方案:通过全局变量 pluginInstance 共享设置数据
let pluginInstance: QiniuImageUploader | null = null;
export function getSettings(): PluginSettings {
return pluginInstance?.settings || DEFAULT_SETTINGS;}
4.5 难点:编辑器实例获取
原因:Obsidian 的编辑器实例获取方式在不同场景下返回值不同
解决方案:使用多种方式尝试获取
private getActiveEditor(): any {
// 方法1: getActiveViewOfType
// 方法2: activeLeaf
// 方法3: 遍历 leaves}
五、经验教训
5.1 定位问题要精准
教训:不要被表面现象迷惑,应该先确认问题的根本原因
案例:批量上传失败时,一开始以为是七牛云 SDK 的问题,实际上是文件名重复导致覆盖
正确做法:
- 先看控制台是否有错误
- 检查文件名是否唯一
- 确认七牛云是否收到多个请求
5.2 不要来回切换方案
教训:频繁切换技术方案会浪费大量时间
案例:在 qiniu SDK 和 fetch 之间来回切换 3 次
正确做法:
- 先分析当前方案为什么失败
- 确认是否有修复可能
- 只有确认无法修复时才切换方案
5.3 保持最小修改原则
教训:只修改必要的代码,不要改动无关部分
案例:为了加边栏功能,改动了很多无关文件导致多次构建失败
正确做法:
- 新增功能尽量用新文件
- 只修改必要的文件
- 其他文件保持原样
5.4 善用控制台日志
教训:遇到问题先看控制台日志,不要盲目猜测
案例:边栏文件列表为空时,没有日志导致无法定位问题
正确做法:在关键位置添加 console.log,确认代码执行到哪一步
5.5 从文件名提取时间更可靠
教训:依赖外部 API 返回的字段可能格式复杂
案例:七牛云 putTime 是纳秒,转换复杂且容易出错
正确做法:
- 文件名已经包含时间戳
- 直接从文件名提取更简单可靠
- 减少对外部 API 格式的依赖
5.6 文件结构建议
最终文件结构:
src/
├── main.ts # 主插件(粘贴/拖拽上传)
├── qiniu_sidebar.ts # 边栏管理(新增)
├── settings.ts # 设置页面
├── uploader/
│ ├── qiniu.ts # 七牛云上传核心
│ └── uploader.ts # 上传器接口
└── lang/ # 多语言支持
styles.css # 样式文件
esbuild.config.mjs # 构建配置
manifest.json # 插件清单
package.json # 依赖管理
六、最终成果
6.1 已实现功能
- ✅ 复制粘贴上传(支持单张和批量)
- ✅ 拖拽上传(支持单张和批量)
- ✅ 文件名格式:
ob-时间戳-原始文件名 - ✅ 右侧边栏文件管理
- ✅ 文件列表显示(带缩略图)
- ✅ 复制链接
- ✅ 插入编辑器
- ✅ 删除文件
- ✅ 分页支持
- ✅ 上传时间显示
- ✅ 按时间倒序排列
- ✅ 文件夹支持
- ✅ HTTPS/HTTP 切换
- ✅ 多区域支持
6.2 技术栈
- TypeScript
- esbuild
- Obsidian API
- 七牛云 SDK(bucketManager)
- fetch API(上传)
6.3 后续优化建议
- 搜索功能:在文件列表中搜索文件名
- 批量删除:支持选择多个文件批量删除
- 图片预览优化:点击缩略图放大查看
- 拖拽排序:在边栏中拖拽调整文件顺序
- 快捷键:为边栏操作添加快捷键
- 统计信息:显示总文件数、总大小等
记录时间:2026-06-26
插件版本:v1.1.6
作者:XIGE基于 tuantu00/obsidian-qiniu-image-uploader 修改