思源笔记 v2.10.3 技术解读:资源文件内容搜索、EPUB 解析与多端体验改进
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
本篇文章以思源笔记(SiYuan)仓库中 v2.10.3 的发布说明(app/changelogs/v2.8.4-v2.12.8/v2.10.3/v2.10.3.md)为骨架,聚焦该版本两大核心主题——资源文件内容搜索的缺陷修复与EPUB 资源文件解析优化,并结合内核源码(kernel/model/asset_content.go等)逐层展开其底层实现。读完本文,你将理解思源笔记"资源内容搜索"的索引、解析与全文检索机制,掌握该版本在编辑器交互、移动端体验、PDF/网络图片处理以及插件 API、鉴权、内核 API 方面的全部变更细节,便于评估是否升级或进行二次开发。
版本概述:围绕"资源内容搜索"的一次集中修复
v2.10.3 是思源笔记 2.8.4 ~ 2.12.8 演进周期中的一个维护型版本。发布说明的 Overview 明确给出了该版本的工作重心:
- 修复了若干与资源文件内容搜索相关的问题;
- 优化了EPUB 资源文件的解析。
同时需要特别说明的是:"搜索资源文件内容"(即对 PDF、EPUB、Office 文档等附件做全文检索)属于付费会员特性,当时处于"早鸟价"阶段。这一会员边界至今仍体现在配置与内核行为中(下文会看到相关常量与索引逻辑),因此在使用该能力前需先确认账户订阅状态。
下文按"改进功能(Enhancement)→ 缺陷修复(Bugfix)→ 开发者(Development)"三组清单展开,先深挖与版本主题最相关的资源内容搜索与 EPUB 解析实现,再逐项解读其余变更。
资源文件内容搜索:从解析到全文检索的完整链路
v2.10.3 对"资源文件内容搜索"的修复并不是孤立的,它建立在内核一套完整的附件内容索引机制之上。理解这层机制,才能看懂该版本修复了什么。整体实现位于 kernel/model/asset_content.go(共约 967 行),负责解析、索引、查询与预览;SQL 持久化与 FTS 表定义在 kernel/sql/asset_content.go、kernel/sql/asset_content_query.go 中。
支持索引的资源类型与解析器注册
内核按文件扩展名注册不同的"资源解析器"(AssetParser接口,见 kernel/model/asset_content.go#L529-L531),只定义了一个方法:
type AssetParser interface { Parse(absPath string) *AssetParseResult }AssetParseResult(kernel/model/asset_content.go#L522-L527)仅含Path / Size / Updated / Content四个字段,即"把二进制附件转换为可索引的纯文本"是全部解析器的共同职责。从源码看,当前注册的解析器至少包含(kernel/model/asset_content.go#L505-L507 附近可见.pdf与.epub注册,同文件更早位置还有对应类型):
TxtAssetParser(.txt 等纯文本,kernel/model/asset_content.go#L533 起);DocxAssetParser(Word,kernel/model/asset_content.go#L605);PptxAssetParser(PowerPoint,kernel/model/asset_content.go#L643);XlsxAssetParser(Excel,kernel/model/asset_content.go#L681,底层使用github.com/xuri/excelize/v2);PdfAssetParser(PDF,kernel/model/asset_content.go#L791,底层使用 go-pdfium);EpubAssetParser(EPUB 电子书,kernel/model/asset_content.go#L931)。
各解析器由AssetsSearcher统一管理(kernel/model/asset_content.go#L387-L397),通过扩展名小写匹配查找解析器。资源文件本身有体积/规模约束,内核在 kernel/model/asset_content.go#L513-L520 定义了硬性上限:
const ( TxtAssetContentMaxSize = 1024 * 1024 * 4 // 文本类 4MB PDFAssetContentMaxPage = 1024 // PDF 最多 1024 页 ) var ( PDFAssetContentMaxSize uint64 = 1024 * 1024 * 128 // PDF 最大 128MB )这些常量意味着:超大附件不会被完整解析入索引,避免索引库被单个文件拖垮。
索引写入:事件驱动 + 全量重建
单个资源文件的索引入口是indexAssetContent(absPath)(kernel/model/asset_content.go#L325-L362),流程为:
- 按扩展名取解析器,无解析器则直接返回;
- 调用
parser.Parse(absPath)得到纯文本内容; os.Stat取文件大小与修改时间;- 构造
sql.AssetContent记录,Path统一换算为以assets/开头的工作空间相对路径(通过util.GetDataAssetsAbsPath()剥离绝对路径前缀); - 先
sql.DeleteAssetContentsByPathQueue(p)删除旧记录,再sql.IndexAssetContentsQueue(...)异步入队写入 FTS 索引表。
触发时机由"资源变更"事件驱动(如新增/修改/删除 assets 下的文件后由 kernel/model/assets_watcher.go 观察触发)。全量重建则通过任务队列完成:fullReindexAssetContent(kernel/model/asset_content.go#L369-L375)会调用sql.InitAssetContentDatabase(true)重建数据库,再执行assetContentSearcher.FullIndex()遍历整个assets/目录(kernel/model/asset_content.go#L399-L429)。
值得注意的一个安全细节:全量索引遍历时会调用IsEncryptedAssetPath(absPath)跳过加密笔记本中的资源文件(kernel/model/asset_content.go#L418-L421),以避免密文污染搜索索引、泄漏加密笔记本内的文件名集合。这与思源笔记"内容加密"的整体设计(见 docs/ENCRYPTED-NOTEBOOK.zh-CN.md)保持一致。
检索模式、排序与分页
查询侧的核心函数是FullTextSearchAssetContent(kernel/model/asset_content.go#L116-L138),其入参完整还原了思源"搜索资源内容"对话框的全部选项:
query:搜索关键字/表达式;types:按扩展名过滤的文件类型集合;method:0=关键字、1=查询语法、2=SQL、3=正则表达式;orderBy:0=按相关度降序、1=按相关度升序、2=按更新时间升序、3=按更新时间降序;page / pageSize:分页,pageCount由命中数上取整得出((matchedAssetCount + pageSize - 1) / pageSize)。
底层数据表为 FTS5 全文索引asset_contents_fts_case_insensitive(大小写不敏感)。关键字与查询语法模式统一走fullTextSearchAssetContentByFTS(kernel/model/asset_content.go#L189-L205),构造形如MATCH 'content:(query)'的查询并对ext IN (...)做类型过滤;正则模式fullTextSearchAssetContentByRegexp(kernel/model/asset_content.go#L151-L165)则对name与content字段使用 SQLREGEXP。排序子句由 kernel/model/asset_content.go#L300-L313 的buildAssetContentOrderBy生成(FTS 相关度即rank)。
SQL 模式(method == 2)直接暴露原始查询能力,由searchAssetContentBySQL执行,并把用户的SELECT *改写为COUNT(path)以复用同一语句统计命中数(kernel/model/asset_content.go#L207-L220 起)——这也是"搜索资源内容"支持高级 SQL 检索的入口。
命中预览与转义:v2.10.3 的修复落点
"资源文件内容搜索预览"是官方 Issue #9073 在 v2.10.3 中改进的对象。预览文本的生成逻辑就在本文件:列表查询使用 FTS 的snippet()截取命中片段,两端以search.SearchMarkLeft / SearchMarkRight作为高亮标记,尾部以'...'截断,长度 64(kernel/model/asset_content.go#L191-L192);单文件详情查询则用highlight()返回完整高亮内容,并会把换行统一替换为<br>(kernel/model/asset_content.go#L60-L87)。
由于资源文件提取出的正文是"原始文本",直接拼入 HTML 预览会出现 HTML 特殊字符(<、>、&等)被当作标签解析的转义问题——这正是 v2.10.3 所称"改进资源文件内容搜索预览转义"要处理的场景:搜索结果在进入前端渲染前需正确转义,既保证高亮标记不被破坏,也避免原始正文中的标签污染页面结构。对二次开发者而言,这意味着在使用上述snippet/highlight输出时,需注意与前端渲染管线的转义约定保持一致。
EPUB 资源文件解析:本次优化的直接对象
EPUB 电子书是内容搜索支持的重点格式之一,v2.10.3 明确提到"改进 EPUB 资源文件解析"(Issue #9072),其实现即 kernel/model/asset_content.go#L931-L967 的EpubAssetParser.Parse:
func (parser *EpubAssetParser) Parse(absPath string) (ret *AssetParseResult) { if !strings.HasSuffix(strings.ToLower(absPath), ".epub") { // 扩展名兜底校验 return } if !gulu.File.IsExist(absPath) { // 文件必须存在 return } tmp := copyTempAsset(absPath) // 复制到临时文件后解析 if "" == tmp { return } defer os.RemoveAll(tmp) f, err := os.Open(tmp) if err != nil { logging.LogErrorf("open [%s] failed: [%s]", tmp, err) return } defer f.Close() buf := bytes.Buffer{} if err = epub.ToTxt(tmp, &buf); err != nil { // 核心:EPUB -> 纯文本 logging.LogErrorf("convert [%s] failed: [%s]", tmp, err) return } content := normalizeNonTxtAssetContent(buf.String()) ret = &AssetParseResult{Content: content} return }可提炼的实现要点如下:
- 双重兜底校验:即便解析器由扩展名路由而来,
Parse内部仍会再次检查.epub后缀与文件存在性,防止被错误调用; - 临时副本隔离:通过
copyTempAsset将 EPUB 复制到临时文件再解析、defer os.RemoveAll(tmp)确保清理,避免解析过程长期占用资源目录下的原文件句柄(对网盘/同步盘场景尤其重要,也与此前 iCloud 路径相关的改进思路一致); - 文本抽取:使用
github.com/88250/epub的ToTxt一次性把整本 EPUB 的书脊(spine)正文输出到bytes.Buffer。EPUB 本质是一个 ZIP 容器,内部以content.opf声明元数据与阅读顺序、以 XHTML/HTML 组织正文——因此ToTxt需要正确解包容器、遍历 spine、剔除章节内的脚本/样式噪声后再拼接正文; - 统一清洗:抽取出的原始文本最后经
normalizeNonTxtAssetContent归一化(如规整空白、折叠多余换行),与 PDF、Office 等非文本解析器走同一套清洗与入索引管线。
v2.10.3 对 EPUB 解析的"改进",从代码演进方向推断主要落在文本抽取的健壮性(例如对目录结构不规范、缺失content.opf或使用非标准 MIME 的 EPUB 文件更宽容),使其能被可靠地纳入全文索引,最终服务于付费的"搜索资源文件内容"特性。
编辑器、浮层与交互改进(Enhancement 其余项)
除资源内容搜索外,v2.10.3 的改进集中在桌面端编辑器交互、窗口/面板行为与移动端细节,逐项说明如下。
浮层与光标行为
- 动态计算浮层层级(#7602):浮层(浮窗/提示层)的 z-index 不再固定写死,而是依据触发位置与嵌套关系动态计算,避免浮层被遮挡或盖住不该盖住的面板。这在"浮窗预览文档块"(#9082)等场景中配合生效——后者同时修复了浮窗预览文档块时自动定位到上一次浏览位置的问题,让预览总是从文档起始位置干净呈现,与主编辑区记忆的浏览位置解耦;
- 划选文本后 Ctrl+M 转换行级公式的光标位置(#9070):选中文本按
Ctrl+M把选区包裹为行级公式后,光标会落在更合理的位置,便于立即继续输入 LaTeX 内容; - 空代码块中可打开块标菜单(PR #9095):此前光标停在无内容的空代码块内时,块标(块操作菜单)无法唤起,本版本修复了该边界条件。
复制、菜单与面板
- PDF 标注复制尺寸一致(#9068):在 Windows 与 macOS 上,从 PDF 复制的标注(高亮/批注)粘贴后保持与源一致的尺寸,不再因平台缩放差异而变化;
复制文本 *系列改进(#9093):选中文本后出现的"复制文本/复制纯文本"等菜单项的复制结果更符合预期;- 禁用菜单项样式(PR #9078):菜单中不可用项的置灰样式得到优化,视觉上更易区分"不可用"与"可用"状态;
- 停靠栏(Dock)显隐状态(#9089):鼠标从应用外部移入、或应用窗口处于非激活状态时,左右侧停靠栏的显示/隐藏判断更合理,不再出现"移入瞬间闪烁/误隐藏";
- iCloud 路径检测改进(PR #9066):macOS 上对 iCloud 同步目录的识别更准确,避免把 iCloud 占位文件或下载中文件误当作本地资源处理,属于资源文件基础设施层面的健壮性增强。
文档加载性能与网络图片
- amd64 平台文档加载性能改进(#9084):针对 Windows/Linux/macOS 的 amd64 架构优化了块加载路径(官方口径为性能改进,代码层面与内核渲染、数据库读取的热路径相关,kernel/model 目录中的
render.go、process.go即承担文档渲染与块处理职责); - "网络图片转换为本地图片"忽略 HTTPS 证书校验(#9080):把网页中的远程图片下载转存为本地资源时,不再因目标站点证书自签名或过期而中断——下载资源文件的相关逻辑位于内核资源处理模块(如 kernel/model/asset.go 对应的下载/转存链路),此改动提升了遇到非正规证书站点时的成功率,代价是放弃了对该次下载连接的服务端证书校验。
搜索与路径提示 UI
- 改进搜索路径提示(#9101):全文搜索界面中"当前搜索范围/路径"的提示布局与文案得到优化,让用户更清楚本次搜索限定在哪些笔记本、路径或资源类型内——与资源内容搜索的"类型过滤"(
types参数)属于同一交互体系。
移动端与跨端修复细节
本版本的移动端(iOS/Android)改动密集,明显针对 iPhone 的可用性问题:
- iPhone 禁止左侧面板顶栏选中内容(#9096):左侧面板顶栏(如笔记本/文档切换栏)在 iPhone 上不再因长按误触发文本选中,交互更跟手;
- iPhone 输入元素边框修复(#9104):补齐 iOS Safari 下
input元素默认边框丢失的问题,避免输入框与背景融为一体、难以辨识; - 移动端云端数据同步图标不再消失(#9090):同步状态图标此前在特定流程(如进入/退出同步)后会从状态栏消失,现可稳定驻留展示;
- 移动端代码块复制按钮失效修复(#9109,Bugfix):触屏下代码块右上角"复制"按钮此前无法正常复制内容,本版本修复了该回归。
缺陷修复清单(Bugfix)
除上文已涉及的 #9109 外,v2.10.3 还修复了以下问题,与编辑器、集市与导入导出流程相关:
| 问题(Issue/PR) | 现象 | 修复意义 |
|---|---|---|
| #9071 | 文档放大(zoom)状态下编辑时光标丢失 | 恢复放大视图下的连续输入体验 |
| #9074 | 集市(marketplace)中包更新后更新按钮不显示 | 修正版本比对与按钮状态刷新逻辑 |
| #9076 | 块引用/超链接文本框内←/→ 无法移动光标 | 恢复引用、链接编辑框的键盘导航能力 |
| #9083 | 导入.sy.zip时块超链接未指向重新生成的块 ID | 导入重写 ID 后,超链接同步重映射到新 ID(内核 kernel/model/import.go 承担 .sy.zip 导入与 ID 重写流程) |
| #9086 | 内核只读模式下无法进入主界面 | 只读模式(可用于备份/审计)下 UI 也能正常挂载 |
| #9106 | 导出 PDF 时"将资源文件作为附件嵌入"失效 | 恢复 PDF 导出时附件资源的嵌入能力 |
其中 #9083 与"资源文件/块 ID"语义直接相关:.sy.zip导入会重新生成块 ID 以保证不冲突,而正文中的超链接若仍指向旧 ID 就会失效,本版本将其纳入重映射范围,属于文档结构一致性修复。
面向开发者的变更(Development)
v2.10.3 对插件体系与内核 API 的调整对二次开发影响最大,值得插件作者与 API 调用方重点关注。
插件 API:openWindow与command.globalCallback
(#9032)本次为插件 API 新增了两个能力:
openWindow:允许插件以窗口形式打开自定义页面/界面(区别于既有的面板、弹窗),可承载更复杂的插件 UI;command.globalCallback:为全局命令注册回调,使插件能响应跨文档、跨场景的全局命令触发。
这两者均是对思源插件运行时(kernel/plugin 目录下api_plugin.go、api_rpc.go、api_event.go等构成插件 API 与事件总线)的能力扩展,前端侧由 app/src/plugin 提供对应类型与封装。
鉴权支持查询参数token
(PR #9069)内核鉴权此前仅接受 Header/固定形式的令牌,现支持在 URL 查询字符串中以token=...传递访问授权码(Access Authorization Code)。这意味着 WebSocket、API 回调及部分无法自定义 Header 的客户端可直接在 URL 中带令牌完成鉴权——但请注意,URL 会被日志、历史记录捕获,该方式更适合受控内网或一次性回调场景。桌面端在"设置 - 关于 - 访问授权码"下管理该令牌(见下文的跟随系统锁屏选项)。
内核 API 改进
/api/file/getFile响应状态码改进(PR #9075):此前对"文件不存在"等错误场景返回的状态码语义不明确,本版本统一为符合 HTTP 语义的状态码(如 404),便于客户端区分"成功/不存在/鉴权失败"。该 API 用于按路径读取资源/文档文件,属于 kernel/api 中文件与资源类接口的范畴;/api/network/forwardProxy改进(PR #9110):内核网络转发代理接口(用于插件/内核侧发起 HTTP 请求时经代理转发)得到增强,返回的状态码与错误信息更可读。
桌面端新增:访问授权码"跟随系统锁屏"
(#9087,Windows/macOS)设置新增"跟随系统锁屏"(Follow system lock screen)开关:开启后,当操作系统进入锁屏状态时,思源对外的访问授权码自动进入锁定态,解锁系统后恢复——避免笔记本在用户离开电脑期间被局域网内其他设备经授权码访问。该项位于设置 - 关于 - 访问授权码页面,属于访问控制(Access Authorization Code,相关实现见 kernel/util/session.go、kernel/conf/user.go 等)在桌面端的安全增强。
版本定位与升级建议
综合来看,v2.10.3 是典型的"稳定性 + 细节打磨"版本:对内修复了资源内容搜索与 EPUB 解析(支撑付费会员特性质量),对外补齐了桌面端交互、移动端可用性以及面向开发者的 API/插件能力。
- 若你正在使用"搜索资源文件内容"会员特性并遇到 PDF/EPUB 内容命中异常、预览转义错乱等问题,v2.10.3 值得升级;
- 若你是插件作者或通过内核 API/WebSocket 鉴权调用思源,请特别关注新增的
openWindow、command.globalCallback与查询参数token鉴权; - 若你运行在 macOS + iCloud 环境或经常处理 PDF 标注、网络图片转本地,本版本对路径检测、证书校验与复制尺寸的修复同样有直接收益。
本仓库的 CHANGELOG.md 汇总了完整版本演进,v2.10.3 的英文、简体中文与繁体中文发布说明分别位于:
- 英文:app/changelogs/v2.8.4-v2.12.8/v2.10.3/v2.10.3.md
- 简体中文:app/changelogs/v2.8.4-v2.12.8/v2.10.3/v2.10.3_zh_CN.md
- 繁体中文:app/changelogs/v2.8.4-v2.12.8/v2.10.3/v2.10.3_zh_CHT.md
希望深入资源内容搜索原理的读者,可继续阅读上述 kernel/model/asset_content.go 全文,并结合 kernel/sql/asset_content.go 中的建表与索引队列实现,梳理"附件变更 → 解析 → FTS 索引 → 命中预览"的完整闭环。
【免费下载链接】siyuanAn open-source, privacy-first, self-hosted knowledge workspace where humans and AI agents work together 开源、隐私优先、自托管的知识工作空间,让人与智能体在此协作项目地址: https://gitcode.com/GitHub_Trending/si/siyuan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考