三股水
Obsidian 七牛云图片上传插件修复与扩展全记录
本文从 Obsidian 撰写发布

Obsidian 七牛云图片上传插件修复与扩展全记录

有了AI的辅助真的爽,经过这次折腾,补上了附件的七牛云问题,再也不用担心电脑出问题存电脑附件丢失了,同时兼容了所有的Obsidian插件,比如发布到typecho,不再去研究TY的插件,咱直接从OB发布,Markdown格式同步到TY,完美。

这次修复其实蛮有难度的,花了我3小时左右,花这么多时间主要是因为AI以及我自己懒了,过于详细AI能力,没有提供思路给它,AI的脑子还是不够聪明的,比如让它新增边栏图片上传时间显示,它硬是要去折腾传到七牛云的返回时间,其实最简单的是上传文件后的时间戳,来回折腾几十遍它都无法搞定,我告诉它把时间戳格式化输出,几十秒解决问题。

这次的插件整合了之前折腾的TY插件的文件上传功能,实现了不错的边栏,复制删除插入等全部实现,全程有提醒日志。

QQ20260626-112846

以下内容是我让AI总结的本次折腾,完整的记录了折腾过程、结果,这样的文件真的很有帮助,这就是AI的魅力哈。

一、原有插件概述

1.1 插件基本信息

  • 插件名称:obsidian-qiniu-image-uploader
  • 原仓库https://github.com/tuantu00/obsidian-qiniu-image-uploader
  • 版本:v1.1.5(两年前更新的插件,没有多少人下载,问题多多)
  • 功能:将 Obsidian 中的图片粘贴上传到七牛云存储,并自动插入 Markdown 链接

1.2 原有特点

  1. 复制粘贴上传:通过 Ctrl+V 粘贴图片自动上传
  2. 拖拽上传:从文件管理器拖拽图片到编辑器自动上传
  3. 七牛云 SDK 支持:使用 qiniu 官方 SDK 进行上传
  4. 多区域支持:支持 z0、z1、z2、na0、as0 等七牛云区域
  5. HTTPS/HTTP 切换:支持协议选择
  6. 自定义文件名前缀:可配置上传文件名的前缀
  7. 时间格式配置:支持自定义文件名中的时间格式

1.3 原有问题

  1. 构建失败proxy-agent 依赖导致 esbuild 打包失败
  2. 仅支持单张上传:同时粘贴多张图片只能成功上传第一张
  3. 文件名冲突:多张图片使用相同时间戳,后上传的覆盖前面的
  4. 依赖过旧:部分依赖包版本过旧,存在安全警告
  5. 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.mjsexternal 数组中添加 "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:多张图片只能上传一张

现象:同时粘贴多张图片,只有第一张成功上传

原因

  1. 事件回调只处理了 files[0]
  2. 文件名生成使用相同时间戳,后上传覆盖前面的

解决方案

  1. 修改事件回调,使用 Array.from 处理所有文件
  2. 新增 uploadMultipleFiles 方法批量处理
  3. 文件名添加索引和随机数确保唯一

关键代码改动
原代码只处理第一个文件,改为遍历所有文件逐个上传

问题5:qiniu SDK 和 fetch 来回折腾

过程

  1. 最初使用 qiniu SDK → proxy-agent 打包失败
  2. 改用 fetch 方式 → 401 认证失败(token 生成算法有 bug)
  3. 重新安装 qiniu SDK → proxy-agent 再次失败
  4. 最终放弃 qiniu SDK,使用 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 批量上传支持

功能:支持同时粘贴多张图片,全部自动上传

实现方式

  1. 修改 customPasteEventCallback 处理所有文件
  2. 新增 uploadMultipleFiles 方法循环上传
  3. 上传过程中显示进度文本 ![Uploading 3 images... xxx]()
  4. 上传完成后批量插入图片链接

效果

  • 从文件夹选中 5 张图片 → Ctrl+C 复制 → 在编辑器中 Ctrl+V 粘贴
  • 所有图片依次上传,最终生成 5 个图片链接

3.2 文件名优化

功能:保留原始文件名,便于识别

格式ob-时间戳-原始文件名

示例

  • 原始文件名:screenshot-2024.png
  • 上传后文件名:ob-1782431380650-screenshot-2024.png

优势

  • 时间戳保证唯一性,避免覆盖
  • 原始文件名便于识别和管理

3.3 右侧边栏管理

功能:新增右侧边栏,提供文件管理功能

包含功能

  1. 文件列表:显示本插件上传的所有图片
  2. 上传按钮:点击选择文件上传(支持多选)
  3. 刷新按钮:刷新文件列表
  4. 复制链接:一键复制图片 URL
  5. 插入编辑器:将图片插入到当前光标位置
  6. 删除文件:从七牛云删除文件
  7. 分页支持:每页 10/15/20/30/50 条可调
  8. 缩略图预览:图片类型文件显示缩略图
  9. 排序:按上传时间倒序排列

操作流程

  1. 点击 Obsidian 左侧边栏的"七牛云管理"图标
  2. 右侧打开文件管理面板
  3. 可查看、上传、复制、插入、删除文件

3.4 上传时间显示

功能:在文件列表中显示上传时间

显示逻辑

  • < 1 分钟:刚刚
  • < 1 小时:X 分钟前
  • < 24 小时:X 小时前
  • ≥ 24 小时:YYYY-MM-DD

实现方式:从文件名中提取时间戳,计算与当前时间的差值


四、技术难点与解决方案总结

4.1 难点:proxy-agent 打包问题

原因qiniu SDK 依赖 urlliburllib 依赖 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 的问题,实际上是文件名重复导致覆盖

正确做法

  1. 先看控制台是否有错误
  2. 检查文件名是否唯一
  3. 确认七牛云是否收到多个请求

5.2 不要来回切换方案

教训:频繁切换技术方案会浪费大量时间

案例:在 qiniu SDK 和 fetch 之间来回切换 3 次

正确做法

  1. 先分析当前方案为什么失败
  2. 确认是否有修复可能
  3. 只有确认无法修复时才切换方案

5.3 保持最小修改原则

教训:只修改必要的代码,不要改动无关部分

案例:为了加边栏功能,改动了很多无关文件导致多次构建失败

正确做法

  1. 新增功能尽量用新文件
  2. 只修改必要的文件
  3. 其他文件保持原样

5.4 善用控制台日志

教训:遇到问题先看控制台日志,不要盲目猜测

案例:边栏文件列表为空时,没有日志导致无法定位问题

正确做法:在关键位置添加 console.log,确认代码执行到哪一步

5.5 从文件名提取时间更可靠

教训:依赖外部 API 返回的字段可能格式复杂

案例:七牛云 putTime 是纳秒,转换复杂且容易出错

正确做法

  1. 文件名已经包含时间戳
  2. 直接从文件名提取更简单可靠
  3. 减少对外部 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 已实现功能

  1. ✅ 复制粘贴上传(支持单张和批量)
  2. ✅ 拖拽上传(支持单张和批量)
  3. ✅ 文件名格式:ob-时间戳-原始文件名
  4. ✅ 右侧边栏文件管理
  5. ✅ 文件列表显示(带缩略图)
  6. ✅ 复制链接
  7. ✅ 插入编辑器
  8. ✅ 删除文件
  9. ✅ 分页支持
  10. ✅ 上传时间显示
  11. ✅ 按时间倒序排列
  12. ✅ 文件夹支持
  13. ✅ HTTPS/HTTP 切换
  14. ✅ 多区域支持

6.2 技术栈

  • TypeScript
  • esbuild
  • Obsidian API
  • 七牛云 SDK(bucketManager)
  • fetch API(上传)

6.3 后续优化建议

  1. 搜索功能:在文件列表中搜索文件名
  2. 批量删除:支持选择多个文件批量删除
  3. 图片预览优化:点击缩略图放大查看
  4. 拖拽排序:在边栏中拖拽调整文件顺序
  5. 快捷键:为边栏操作添加快捷键
  6. 统计信息:显示总文件数、总大小等

记录时间:2026-06-26
插件版本:v1.1.6
作者:XIGE基于 tuantu00/obsidian-qiniu-image-uploader 修改

😊
提交