给Obsidian减肥:网盘pdf在线阅读

在使用Obsidian学习的时候发现,仓库里的PDF文件变得越来越多,很多课件、电子书、论文。。。有的PDF文件甚至有上百兆字节。这就导致我的空间本就不大的iPad存储空间被大量占用,虽然可以手动删除,但是删除之后相关的笔记链接也失效了。所以,我就想:

  1. 能不能不需要下载PDF,即可以阅读还可以做笔记。
  2. 点击引用链接后还能自动打开PDF。

经过一番探索,发现是可以的,Sync Vault 插件中就按照这里介绍的方法实现了一个云盘浏览器,点击pdf文件图标后可以在线阅读pdf,加载结束之后和本地阅读体验并无二致。

魔法解密

obsidian内置了PDF.js,在点击pdf文件打开的时候会触发pdf视图,但是obsidian自身又封装了一个pdf viewer - ObsidianViewer对象,这个对象通过createObsidianPDFViewer方法创建。
简化的流程是这样的:

flowchart LR
点击pdf文件 --> 加载pdfjsViewer到window.pdfjsViewer --> 创建ObsidianViewer --> 打开pdf

于是我们就可以通过拦截createObsidianPDFViewer方法来修改创建的ObsidianViewer,进而支持从网盘加载文件。

拦截createObsidianPDFViewer

plugin.register(around(window.pdfjsViewer, {
	createObsidianPDFViewer(old) {

	return function (options: any): ObsidianViewer {
		const obsidianViewer = old.call(window.pdfjsViewer, options);
		// NOTE: 在这里继续通过cross对obsidianViewer对象进行拦截,只需要关心open方法就可以了
	}
}

什么时候拦截呢?参考上面的流程图,在挂载pdfjsViewer到window的时候触发拦截是绝好的时机,此时调用上面的plugin.register()方法,那么在后续通过open方法打开pdf的时候就能调用我们自己的代码了。

修改open,让PDF.js打开网盘文件

原生的open方法传入pdf文件地址或者文件内容。obsidian在打开本地pdf文件的时候会传入pdf文件的路径。我们首先改造这个路径,让它支持百度网盘。

  • dummy file
    对应路径下不保存真实的pdf文件,而保存一份dummy file(参考PDF++实现),dummy file中存储了百度网盘中pdf文件的下载地址,文件大小。

  • PDFDataRangeTransport加载云端文件

loadingTask = old.call(obsidianViewer, {
	rangeChunkSize: RANGE_CHUNK_SIZE,
	range: rangeTransport,
});
return loadingTask;

接下来实现rangeTransport中的requestDataRange方法:传入begin和end,表示获取的数据range的起始位置和结束位置,返回ArrayBuffer。

具体实现中通过obsidian内置的requestUrl从百度网盘下载地址中获取range对应的内容即可。如此,在打开PDF的时候,我们的RangeTransport便会自动按需加载云端的pdf内容数据。

Sync Vault 正是通过这样的方法,提供了在线PDF的无缝阅读体验,不论是通过obsidian自带的pdf阅读器还是PDF++,都可以不用下载PDF到本地即可阅读整理文献内容。相当于给Obsidian减了个肥,以后不用一直保存本地的PDF了,直接丢进网盘中即可。最终的操作界面就像下面这个样子,如同在本地访问文件一样。

1 个赞

好文,我目前只是把图片放在云端不下载到本地,pdf 看看什么时候有空实现这个效果。不过 sync vault 介绍貌似没这个功能特性啊