- 桌面应用
【免费下载链接】QOwnNotes
QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.
QOwnNotes 以纯文本 Markdown 文件为核心,官方在Note / Import菜单中内置了 Evernote(ENEX)与 Joplin 两套图形化导入工具,并通过脚本面板支持 Obsidian(OFM)与 GitHub Flavoured Markdown(GFM)之间的链接风格双向转换;其余常见笔记软件(Google Keep、Synology Note Station、Tomboy 等)也有对应的命令行转换路径。本文以官方文档 importing-notes.md(其英文原文位于 webpage/src/getting-started/importing-notes.md)为骨架,结合仓库源码逐一拆解每种来源的导入原理、对话框选项与注意事项,帮助你安全、无损地把历史笔记迁入 QOwnNotes。
总览:导入入口与导入后的刷新机制
所有内置导入功能都挂在主菜单Note / Import下。从源码看,主窗口为每次导入单独实例化对话框,并在导入完成后重建笔记索引:
MainWindow::on_actionImport_notes_from_Evernote_triggered()(src/mainwindow.cpp):创建EvernoteImportDialog,若getImportCount() > 0则调用buildNotesIndexAndLoadNoteDirectoryList(true, true)刷新笔记列表;MainWindow::on_actionImport_notes_from_Joplin_triggered()(src/mainwindow.cpp):创建JoplinImportDialog,行为与 Evernote 相同。
因此,导入完成后笔记会自动出现在当前笔记文件夹中,无需手动刷新。Evernote 与 Joplin 的导入都是“增量式”的:每条笔记依次写入磁盘并计数,进度在对话框中实时展示。
从 Evernote 导入(ENEX 文件)
Evernote 是最常见的迁移来源之一。QOwnNotes 提供了完整的图形化导入对话框,入口为Note / Import,用于导入 Evernote 桌面客户端导出的.enex文件。
第一步:导出 ENEX 文件
在 Evernote 桌面客户端中,选中一条或多条笔记,然后在File 菜单中点击 “Export notes…”即可得到.enex文件。该提示也直接写在了导入对话框的界面文案中(见 evernoteimportdialog.ui)。
第二步:在对话框中配置导入选项
在 QOwnNotes 中打开Note / Import下的 Evernote 导入项,界面提供以下控制项:
| 选项 | 默认值 | 说明 |
|---|---|---|
选择.enex文件 | — | 通过 “Select file” 按钮选择;上次选择的文件路径会被记住(设置键EvernoteImport/LastFile) |
| Import images | 勾选 | 将 ENEX 中的图片资源导入为 Markdown 图片引用 |
| Import attachments | 勾选 | 将 ENEX 中的附件资源导入并生成附件链接 |
| Import metadata as YAML front matter | 不勾选 | 勾选后元数据以 YAML front matter 形式写入笔记头部,否则以 Markdown 表格形式追加在标题之后 |
| Metadata to import(树形勾选) | 部分勾选 | 精细控制要导入的元数据字段 |
元数据树支持勾选的字段包括:创建日期(created)、更新日期(updated),以及 Evernote 的note-attributes下的主题日期(subject-date)、纬度(latitude)、经度(longitude)、海拔(altitude)、作者(author)、来源(source)、来源 URL(source-url)、来源应用(source-application)、提醒顺序(reminder-order)、提醒时间(reminder-time)、提醒完成时间(reminder-done-time)、地点名称(place-name)、内容类别(content-class)、应用数据(application-data)。这些字段在 evernoteimportdialog.cpp 中逐一注册。对话框会记住你每次的勾选状态(设置键EvernoteImport/ImageImportCheckBoxChecked、EvernoteImport/AttachmentImportCheckBoxChecked、EvernoteImport/MetaDataAsFrontmatterCheckBoxChecked、EvernoteImport/MetaDataUnCheckedList)。
源码级原理:ENEX 是如何被“消化”的
ENEX 本质是包含<note>元素的 XML 文档。导入流程(evernoteimportdialog.cpp)用QXmlStreamReader逐 token 解析:
- 解析笔记主体:
parseNote()读取title、content、tag等元素。Evernote 有时会在 CDATA 前塞入空白字符导致内容被误读,源码针对这种情况做了二次读取兼容处理(evernoteimportdialog.cpp)。 - 提取资源:
parseResource()从<resource>中解析mime、recognition(内含objID用于与正文<en-media>标签的hash关联)、base64 编码的data与resource-attributes/file-name。若没有objID,会用 MD5 对 base64 数据自行生成哈希作为对象 ID(evernoteimportdialog.cpp)。 - 导入图片:
importImages()用正则匹配<en-media ... type="image/...">标签,将 base64 数据解码后通过Note::importMediaFromBase64()写入 media 目录,并在正文中替换为 Markdown 图片引用;未在正文中出现的图片也会追加到笔记末尾,避免丢失(evernoteimportdialog.cpp)。 - 导入附件:
importAttachments()的处理方式类似:base64 解码后先写入临时文件,再通过Note::getInsertAttachmentMarkdown()生成附件链接(evernoteimportdialog.cpp)。 - HTML 转 Markdown:Evernote 正文是 HTML,
Utils::Misc::transformEvernoteImportText()(src/utils/misc.cpp)会先识别 Evernote 的代码块 div(-en-codeblock)并转换为```围栏代码块,然后把<div>替换为换行、转义超链接,最后调用htmlToMarkdown()完成整体转换,并清理残留 HTML 标签与多余空行。注意该转换必须在图片/附件导入之后进行,否则正文中的en-media引用无法被正确识别替换(evernoteimportdialog.cpp)。 - 写入元数据:
compileMetaDataText()根据是否勾选 front matter 选项,将元数据编译为 YAML front matter(多行值使用|-块标量,单行值做单引号转义)或 Markdown 表格(evernoteimportdialog.cpp)。 - 标签关联:
tagNote()对每个 tag 名称执行Tag::fetchByName(),不存在则创建,再linkToNote()关联到笔记(evernoteimportdialog.cpp)。
此外,在 Windows 上导入时会把标题中的—、|、/、\替换为-,并移除#、?、引号等不适宜作文件名的字符(evernoteimportdialog.cpp)。遇到无效 XML 时导入会安全停止并弹出提示,不会崩溃(evernoteimportdialog.cpp)。
从 Joplin 导入
Joplin 的导入同样是图形化对话框,入口为Note / Import。与 Evernote 不同,Joplin 导入的目标是“RAW - Joplin Export Directory”——即 Joplin 的原始导出目录,而不是单个文件。
前置准备与选项
- 如果你手里只有 “JEX - Joplin Export File” 压缩包,请先将其解压到某个目录,再选择该目录作为导入来源(对话框界面文案见 joplinimportdialog.ui)。
- 导入选项(默认值均为勾选):Import folders、Import tags、Import images、Import attachments、Import metadata。
- Import folders 的启用条件:只有当当前笔记文件夹开启了“显示子文件夹”时才可用;未开启时该项会被禁用并提示(joplinimportdialog.cpp)。
- 对话框同样会记住上次选择的目录(设置键
JoplinImport/LastDirectory)以及各复选框状态(JoplinImport/FolderImportCheckBoxChecked等)。
源码级原理:Joplin 原始导出的数据结构
Joplin 的原始导出目录中,笔记、文件夹、资源、标签等都以.md文件平铺存放,靠文件内的type_字段区分(joplinimportdialog.cpp):
type_值 | 含义 | 处理方式 |
|---|---|---|
| 1 | 笔记(note) | 收集到_noteData,后续逐条导入 |
| 2 | 文件夹(folder) | 收集到_folderData,先于笔记导入,保证父子文件夹已就绪 |
| 4 | 资源(resource,含mime: image/...者为图片) | 分别收集到_imageData/_attachmentData |
| 5 | 标签(tag) | 收集到_tagData |
| 6 | 标签与笔记的关联 | 收集到_tagAssignmentData |
导入顺序非常讲究:先importFolders()递归创建子文件夹,再逐条importNote()。文件夹导入通过parent_id递归定位父级,使用NoteSubFolder::fetchByNameAndParentId()复用已存在的子文件夹(joplinimportdialog.cpp)。
importNote()的核心行为包括:
- 标题取文件首行并去掉前面的数字编号前缀(如
001.),正文用Note::createNoteHeader()生成 QOwnNotes 笔记头部; - 勾选 “Import metadata” 时,把 Joplin 的元数据块(含
id)包装成 YAML front matter 写入笔记; - 勾选 “Import folders” 时,根据
parent_id把笔记归入对应子文件夹; - 勾选 “Import tags” 时按
note_id/tag_id关联创建并绑定标签; - 图片处理
handleImages()支持三种 Joplin 内链语法:alt、alt以及<img src=":/imageId" />(joplinimportdialog.cpp)。资源文件从导出目录的resources/<id>.<ext>读取;若元数据中没有file_extension,会退回到按id前缀在 resources 目录中模糊匹配(joplinimportdialog.cpp); - 附件处理
handleAttachments()用(?<!!)负向后行断言区分普通链接与图片链接,同一资源在多次引用时会复用已导入的副本,而不是生成重复文件(joplinimportdialog.cpp)。
值得一提的是,applyJoplinTimestamps()会把 Joplin 元数据中的updated_time(ISO 8601 带毫秒格式)写回磁盘文件的修改时间,使 “按日期排序” 能正确反映笔记的真实历史,而不是导入时刻(joplinimportdialog.cpp)。
测试佐证
仓库的单元测试对 Joplin 导入的关键路径做了回归保护,例如testHandleImagesDoesNotOrphanRepeatedIdenticalImageTagInSameNote(同一笔记中重复出现的相同图片标签不会产生“孤儿”文件)以及testDuplicateTitleInNonActiveSubfolderGetsSuffixedNotOverwritten(非活动子文件夹中重名笔记应自动加后缀而非互相覆盖),均位于 tests/unit_tests/testcases/app/test_notes.cpp。JoplinImportDialog通过friend class TestNotes向测试暴露私有方法(src/dialogs/joplinimportdialog.h)。
从 Obsidian 导入
Obsidian 使用“Obsidian Flavoured Markdown(OFM)”,其内部链接大量使用wikilink语法。迁移思路是先让 Obsidian 放弃 OFM 链接,再让 QOwnNotes 把剩余的 OFM 链接转换为标准 Markdown:
- 在 Obsidian 中安装社区插件“Better Markdown Links”,将所有的
wikilink与 Obsidian 风格链接转换为标准 Markdown 链接,使库以标准 Markdown 链接运作; - 在 QOwnNotes 中打开同一批文件(同一个 vault),安装 QOwnNotes 脚本link-importer-and-converter-for-obsidian-github-pandoc-markor-and-others(该脚本位于官方 scripts 仓库,安装后在脚本面板中会提供导入按钮);
- 在脚本面板中点击“ObsidianImport”按钮,转换全部文件。
从 QOwnNotes 导出到 Obsidian并不需要——因为 QOwnNotes 本身就以标准 Markdown 文件工作,Obsidian 能直接读取同一批文件。
重要警告:插件 “Better Markdown Links” 在 Obsidian 打开时、或当文件夹/文件/标题被重命名时,会静默地改写链接。因此,每次在 Obsidian 中修改过文件后,都应该回到 QOwnNotes 重新点击 “ObsidianImport” 按钮,否则部分链接可能无法再被 QOwnNotes 识别。
GFM 与 QOwnNotes/Obsidian 的链接风格转换
GitHub Flavoured Markdown(GFM)与 QOwnNotes/Obsidian 在“指向标题的链接”上存在系统性的语法差异,这是跨软件迁移最容易被忽视的坑:
- GFM使用连字符风格(hyphen-style)的标题片段:空格被转换为连字符。例如对标题
## Heading with spaces,引用写作notelink(原文示例即如此,注意大小写会被归一化); - QOwnNotes 与 Obsidian(OFM)使用urlencoded(百分号编码)的标题片段:空格被转换为
%20且保留大写字母,同一标题的引用写作notelink。
双向转换操作
在 QOwnNotes 中打开文件后,安装同一脚本link-importer-and-converter-for-obsidian-github-pandoc-markor-and-others,然后在脚本面板中点击“GFMImport”按钮,即可批量把 GFM 链接转换为 QOwnNotes/Obsidian 风格(或反向)。
导出方面:从 QOwnNotes 导出为 GFM 风格支持单页导出(适合随后用 pandoc 做连续转换)或全量导出。
信息丢失警告(重复标题场景):假设
β_note.md内存在多个相同标题,如同一篇笔记里有多个## Advantages。GFM 允许用#advantages、#advantages-1、#advantages-2… 分别引用它们,但QOwnNotes 与 Obsidian 都不支持这种“同名标题序列引用”——两者都要求同一文档内的标题名称唯一。转换之后,这些链接将全部指向第一个出现的同名标题(在长文档中尤其致命)。规避方法:转换前先给标题补上差异化文字,并更新对应的链接,确保标题唯一。
其他格式:使用 jimmy 命令行工具
对于 Google Keep、Synology Note Station 等更多来源,官方推荐使用命令行工具jimmy将它们先转换为 Markdown:
- jimmy 支持多种格式到 Markdown 的转换,包括 Google Keep、Synology Note Station 等;
- 为了尽可能保留元数据,官方建议按照 jimmy 的 QOwnNotes 专属导入说明应用额外调整(additional tweaks),例如文件名规则、front matter 字段映射等;
- 若转换过程遇到问题,可在 jimmy 的 GitHub 仓库提交 issue。
这条路径适合“一次性批量迁移”:先用 jimmy 产出 Markdown 文件,再把这些文件放进 QOwnNotes 的笔记文件夹即可,无需经过任何导入对话框。
Google Keep:Google Takeout + keep_to_markdown.py
Google Keep 没有官方导出格式,但可以通过 Google Takeout 导出数据,再用社区脚本转换成 Markdown:
- 使用Google Takeout下载 Keep 任务/笔记数据;
- 解压下载的归档文件;
- 将
keep_to_markdown.py脚本下载到解压出的 Keep 文件夹的旁边; - 执行转换命令:
python keep_to_markdown.py Keep out/其中Keep是包含 JSON 格式笔记的文件夹,out/目录若不存在会自动创建。转换完成后,把out/中的 Markdown 文件作为笔记文件夹接入 QOwnNotes 即可。
Tomboy:通过 Trombone 转换为 ENEX 再导入
Tomboy 笔记可以用脚本Trombone转换成 Evernote 的.enex文件,再复用上文介绍的 Evernote 导入流程。官方文档给出的完整操作步骤如下:
- 安装 Python 2(Trombone 依赖 python2,可一并安装
python-is-python2,之后可再移除):
sudo apt install python2 python-is-python2建议在 chroot 或 Docker 容器中执行,以隔离老旧 Python 环境带来的风险。
- 下载 Trombone,解压后进入其目录并编译安装:
cd ~/Downloads/trombone-master sudo make sudo make install- 进入 Tomboy 笔记所在目录:
cd ~/.local/share/tomboy/- 把所有
.note文件批量转换为单个 ENEX 文件:
find ./ -type f -name '*.note' -print0 | xargs -0 trombone > EXPORT.enex若某些笔记触发 unicode 错误,可将对应笔记移除后重新执行转换,直到看到
Saving...提示,即可得到EXPORT.enex。在 QOwnNotes 中通过 Evernote 导入对话框导入该文件。由于 Tomboy 本身没有标签/富文本等概念,官方建议取消勾选除创建/修改日期以外的所有导入属性,只保留时间信息,避免产生无意义的内容。
导出为 Markdown 的补充能力
虽然本文主题是导入,但理解 QOwnNotes 的导出能力有助于设计“双向往返”迁移:Note::exportToPath()(src/entities/note.cpp)可以把当前笔记连同 media 图片一起导出为 Markdown 文件——导出时会复制媒体文件,并把正文中的media/路径片段改写成相对目标目录的形式。对应菜单项为 “Export note as Markdown file”。这与 Obsidian/GFM 部分提到的“单页导出配合 pandoc”是同一能力的两种用法。
迁移路线总结
| 来源 | 方式 | 关键要点 |
|---|---|---|
| Evernote | 内置对话框(Note / Import) | 需.enex文件;可选图片/附件/YAML front matter/逐项元数据 |
| Joplin | 内置对话框(Note / Import) | 需 RAW 导出目录(JEX 需先解压);可选文件夹/标签/图片/附件/元数据;自动恢复时间戳 |
| Obsidian(OFM) | Better Markdown Links 插件 + QOwnNotes 脚本 “ObsidianImport” | 每次在 Obsidian 修改后需重新执行转换 |
| GFM(VS Code/Codium、GitHub、Markor、Typora、Pandoc 等) | QOwnNotes 脚本 “GFMImport” | 注意连字符 vs 百分号编码标题片段的差异;重复标题需先人工唯一化 |
| Google Keep / Synology Note Station 等 | jimmy 命令行工具 | 按 jimmy 的 QOwnNotes 指南应用额外 tweaks 保留元数据 |
| Google Keep | Google Takeout +keep_to_markdown.py | python keep_to_markdown.py Keep out/ |
| Tomboy | Trombone 转 ENEX | Python 2 环境;导入时仅保留日期属性 |
执行任意一种导入后,笔记都会即时刷新进当前笔记文件夹;所有内置对话框的选项状态都会被记住,方便你分批次、可重复地完成迁移。若你在迁移中遇到格式怪异的链接,请优先回到本指南的 GFM/Obsidian 章节排查标题编码与唯一性问题。
- 桌面应用
【免费下载链接】QOwnNotes
QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.
相关推荐
QOwnNotes 笔记导入完全指南:从 Evernote、Joplin、Obsidian、GFM 到各种 Markdown 格式
QOwnNotes 笔记导入完全指南:从 Evernote、Joplin、Obsidian、GFM 到各种 Markdown 格式 导读 :QOwnNotes
桌面应用QOwnNotes 笔记导入实战:Evernote 与 Joplin 导入机制、Obsidian 及 GFM 链接转换详解
QOwnNotes 笔记导入实战:Evernote 与 Joplin 导入机制、Obsidian 及 GFM 链接转换详解 本篇指南聚焦 QOwnNotes 的
桌面应用Solarized for Tmux:终端复用器的色彩协调配置
Solarized for Tmux:终端复用器的色彩协调配置 Solarized 是一套精准的色彩方案,专为多种应用(包括终端和 Vim 等)设计,同时支持深
开发者工具设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考