ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Readest 备份体积膨胀与孤儿书籍文件治理:从 issue 5837 到 `getOrphanedBookEntries` 的完整实战解析

Readest 备份体积膨胀与孤儿书籍文件治理:从 issue 5837 到 `getOrphanedBookEntries` 的完整实战解析 桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载本文以 Readest 仓库内.claude/memory/backup-orphan-book-files-5837.md为骨架结合 备份服务 与 缓存工具 的源码与测试完整还原 Android 备份 zip 携带大量不在书库中的书籍文件这一问题的根因、三条孤儿目录产生路径、以及备份导出与 Manage Cache 两侧的修复方案。读完你可以理解 Readest 的Books/hash/目录布局、deletedAt软删除语义并掌握如何通过仓库源码定位与验证此类磁盘与书库不一致问题。问题现场备份 zip 里出现了书库里不存在的书issue #5837 由一位 Android 16、Readest 0.12.1 的 OPDS KOSync 用户报告备份生成的 zip 中携带了数百个在书库界面中根本不存在的 EPUB/PDF 文件且刷新元数据Refresh Metadata与管理缓存Manage Cache都无法让备份变小。这个现象在 Readest 的文件模型下并不难理解所有书籍内容都存放在用户数据目录的Books/hash/子目录下hash 即书籍记录的主键而备份逻辑在此之前的行为是直接遍历整个Books/树、把所有文件都打进 zip。只要磁盘上存在某个没有任何存活书库记录引用的Books/hash/目录该目录就会静默进入备份。而由于恢复逻辑restoreFromBackupZip自 #3571 起有意导入这些孤儿 hash 目录来抢救数据这一隐患长期没有被发现——备份把垃圾带出去恢复又把它带回来两端的宽容互相掩盖。备份侧根因addBackupEntriesToZip从不询问书库修复前的关键缺陷位于 addBackupEntriesToZip对应 PR #5851 的改动说明备份流程先通过appService.loadLibraryBooks()加载书库但导出文件时完全没用这份数据它调用appService.readDirectory(booksDir, None)枚举Books/目录下的所有文件仅排除一个硬编码的LIBRARY_META_FILES集合结果就是只要Books/hash/没有对应的活跃记录deletedAt为空的行该目录下的所有文件都会被导出。这也是为什么用户观察到的 zip 里数百个 EPUB/PDF与书库 UI 完全对不上——它们本就不该出现在书库中。孤儿目录的三条产生路径均有代码依据根据文档与仓库代码交叉验证Books/hash/中出现无主文件目录主要来自三条路径路径一OPDS 自动下载复活墓碑行最可能的主因OPDS 自动下载在重新拉取文件时只在内存中复活一条已被墓碑化tombstoned的书籍记录磁盘上的行仍保留deletedAt时间戳。相关的修复#5665commit34922b172不在 0.12.1 发布版中文档明确记载git merge-base --is-ancestor验证为否因此 0.12.1 用户仍会持续产生这类孤儿目录报告中大概率走的正是这条路径。路径二导入中断的 kill windowimportBook会先把文件复制进Books/hash/之后调用方才执行saveLibraryBooks保存记录。Android 进程在两步之间被系统杀死时磁盘上留下完整文件、书库中却没有行。文档将其称为 files-before-row 窗口OPDS 按目录批量持久化与备份恢复同样存在此窗口。路径三云同步墓碑永远不删本地文件仓库中没有任何同步路径调用appService.deleteBook真正删除书籍文件的只有三个入口书库页面的用户操作、transferManagercloud删除动作与deleteLibraryServicepurge清除动作。因此从另一台设备拉回的deletedAt墓碑只会隐藏行、保留本地 EPUB。文档还特别指出一个关键设计普通删除local动作只移除受管书文件刻意保留cover.png与config.json以便重新下载时无缝续传——所以每一个墓碑行都仍然拥有一个目录。deletedAt行始终留在 store 的library数组中只是被visibleLibrary过滤掉见 libraryStore.ts。备份侧修复只导出活跃书籍的目录PR #58512026-08-24 合入squash commitded443512对备份逻辑的核心改动如下这些规则在 backupService.ts 中可直接看到// 只保留未删除!deletedAt书籍的 hash const liveHashes new Set(books.filter((b) !b.deletedAt).map((b) b.hash)); const isExported (path: string) { const dir getBookDirOfPath(path); return !!dir (books.length 0 || liveHashes.has(dir)); };liveHashes白名单Books/下只有属于活跃非deletedAt书籍的hash/目录才会导出移除LIBRARY_META_FILES硬编码根级元数据文件如library.json.bak因getBookDirOfPath解析不到目录而自然被排除空库回退当书库一行都没有时例如library.json损坏、safeLoadJSON返回[]books.length 0使isExported恒真恢复导出所有目录——此时磁盘是唯一副本恢复端的孤儿导入restoreFromBackupZip可以据此重建整个书库Windows 路径归一化readDirectory返回主机分隔符路径Windows 下是反斜杠hash\cover.png导出前统一replace(/\\/g, /)为 zip 条目名保证备份可在任意平台恢复issue #4703 的回归保障排除离线 Audiobookshelf 音频abs-offline/下的音频#6256可重新下载且体积可达 GB 级被isAbsOfflineEntry过滤避免备份把可重建的音频逐文件读入内存。配套的回归测试 backup-orphan-files.test.ts 用LIVE_HASH / DELETED_HASH / ORPHAN_HASH三个假 hash 验证跳过无行目录与软删除书籍目录、空库时导出所有目录、全部软删除时导出空集、Windows 反斜杠路径正确匹配、离线音频不导出。同时 backup-windows-paths.test.ts 的 fixture 现在持有BOOK_HASH防止路径解析回归。恢复侧保持不变旧备份中的孤儿 hash 目录仍会被导入依据 zip 中library.json里不存在的 hash 判断因此历史备份依旧可以完整恢复。缓存侧修复getOrphanedBookEntries的完整回收规则备份侧不再导出解决了脏数据外流但磁盘上的孤儿文件本身还需要一个回收入口。修复在 cache.ts 中新增了getOrphanedBookEntries(appService, books)返回CacheEntry[]base为Books把孤儿文件并入 Manage Cache 的扫描与清除。其规则集合是本次评审加固review hardening的重点全部有测试覆盖1. 绝对路径扫描触发原生快路径扫描Books/必须用appService.resolveFilePath(, Books)拿到绝对路径、再以 baseNone调用readDirectory。若按 base-relative 读取会解析成 Tauri 的 baseDir错过 Rust WalkDir 的原生快路径退化为每个条目一次 IPC 往返。注释明确说明getCacheEntriesCache/Temp 的小目录仍保留慢路径是既有行为。2. 副产物永不回收KEPT_SIDECARS new Set([cover.png, config.json, nav.json])加上audiobook/**目录下的配对音频普通删除刻意保留封面、进度与笔记临时打开的文件也需要config.json承载进度config.json引用配对音频只有removePairedAudiobook能清除该关联。因此任何无主目录中的这些文件都不可回收。3. 新鲜度守卫ORPHAN_SETTLE_MSORPHAN_SETTLE_MS 60 * 60 * 10001 小时目录 mtime 距今不足 1 小时则跳过。这覆盖importBook的 files-before-row 窗口、OPDS 按目录持久化与恢复的批量写入——没有任何导入器暴露 in-flight 信号只能靠时间窗兜底。守卫通过新增的AppService.stats()实现见 types/system.ts每个候选目录只 stat 一次测试断言stats被调用且仅调用一次stat 失败、未知或空 mtime 一律按未稳定跳过绝不靠猜测提供删除项。4. 零行书库 UNKNOWN而非 emptylibrary.loaded library.books.length 0才纳入孤儿扫描getClearableEntriescache.ts未加载的书库会把每本磁盘书都误判为孤儿library.json损坏时safeLoadJSON返回[]此时这些 hash 目录是唯一副本Manage Cache 不提供任何孤儿项而备份端回退导出所有目录。两端的UNKNOWN 不动作原则是一致的。5. 活行保护优先于重复墓碑liveHashes用Set而非last-wins Map即便遗留的library.json对同一 hash 同时存在活行与墓碑行加载时不去重任何一条活行都保护整个目录。6. 清除时二次校验与失败上报清除使用扫描时快照state而非重新扫描确认前若有同步/导入为某目录补上了活行withoutLiveBookEntries会把这些条目从删除集合中剔除。删除逐个进行、失败计数不中断循环failed 0时复用既有 i18n keyFailed to delete {{count}} file(s)上报CacheManagerWindow.tsx。清除后遗留空的Books/hash/目录是无害的——AppService没有removeDir能力。Manage Cache 的 UI 集成与用户提示CacheManagerWindowAdvanced Settings Manage Cache移动端专用由 SettingsMenu.tsx 打开把孤儿文件折入扫描与清除流程状态机scanning → idle → confirming → clearing → done/error扫描与清除均走进度条扫描时从useLibraryStore读取library与libraryLoaded传入getClearableEntries孤儿仅在库已加载且非空时计入存在孤儿时额外显示Includes {{count}} orphaned book file(s) not in your library确认语句升级为更强的This will delete all cached files and orphaned book files not in your library. This cannot be undone.CacheManagerWindow.tsx移动端来源iOS/Android 均清除Cache与Temp两个 baseiOS 额外清除Documents/InboxOpen in Readest 留下的已导入副本否则永久残留i18n2 个新 key 通过脚本按各 locale 的{{count}} files复数后缀集追加到 34 个语言文件en 获得_one/_other变体。组件级测试 cache-manager-window-orphans.test.tsx 与工具级测试 cache.test.ts含getOrphanedBookEntries、withoutLiveBookEntries、getClearableEntries三组用例共同锁定了上述行为。修复的验证、状态与遗留问题文档明确记载PR #5851 于 2026-08-24 以ded4435123 个 commit 的 squash修复、评审加固、覆盖测试合入完整测试套件 809 个文件 / 9979 个测试全部通过当时尚未进入任何发布 tag最新为 v0.12.1将随 #5665 一起发布真机验证Xiaomi当时仍在进行中。这些属于文档记录的项目事实发布情况请以仓库最新版本为准。文档同时记录了三条未完成事项可作为后续阅读源码时的线索没有启动时对账逻辑磁盘Books/vs 书库的应不应该长期存在的诉求——孤儿回收仍依赖用户主动打开 Manage Cache桌面端没有 Manage Cache 入口桌面用户暂时无法在应用内回收孤儿文件面向报告者的诊断方法在备份 zip 的library.json中 grep 孤儿 hash——存在带deletedAt对应路径一/三墓碑复活不存在则对应路径二导入中断。小结从一次备份事故读出的文件生命周期issue #5837 的完整修复链条可以概括为三句话备份端只带活数据liveHashes白名单 空库回退缓存端回收无主数据getOrphanedBookEntries的五条保护规则恢复端保持宽容孤儿导入原样保留以兼容旧备份。对 Readest 的使用者而言若备份 zip 过大升级到包含该修复的版本后可在移动端通过高级设置 → 管理缓存安全回收孤儿文件对想要深入代码的读者backupService.ts、cache.ts 与两份配套测试是理解Books/hash/目录模型与deletedAt软删除语义的最佳入口。赞分享桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载相关推荐探索二进制体积膨胀cargo-bloat探索二进制体积膨胀cargo bloat cargo bloat 是一个强大的工具专门用于检测 Rust 应用程序可执行文件中的空间占用大户。它支持 ELF开发工具告别Vue项目体积膨胀webpack-bundle-analyzer组件级优化实战指南告别Vue项目体积膨胀webpack bundle analyzer组件级优化实战指南 你是否曾遇到Vue项目打包后体积暴增首屏加载慢到让用户流失是否在优FlashAttention终极指南5步搞定高性能注意力机制编译与优化FlashAttention终极指南5步搞定高性能注意力机制编译与优化 在当今大模型时代Transformer架构已成为AI研究的核心支柱然而其核心组件—人工智能大模型算子库上一篇微信聊天记录永久保存终极指南WeChatMsg免费工具三步搞定下一篇零信任时代的LocalAI数据安全从传输加密到存储防护全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表