开源了一个"先预览、能反悔"的文件整理工具,聊聊背后的工程细节

正文

大家好,我是 Rain9。

先上链接,后面都是干货:

Luma

Luma 是一个本地优先的文件空间分析与整理工具:

  • 扫描你选定的目录,只读元数据,在本机建 SQLite 索引

  • 空间分类、大文件、重复文件候选、开发项目识别、历史对比

  • FTS5 + BM25 的本地搜索,支持"大于 1GB 的最近一年的 PDF"这类确定性条件

  • 文件管理器支持重命名、移动、复制、废纸篓

  • 规则整理走 dry-run:先生成完整清单,你确认才动手,事后可撤销(重启也能撤)

  • 中英文界面,macOS / Windows 双平台

没有 AI,不上传任何东西,也没有后台常驻。今天主要聊聊功能设计和开发中踩的坑。

为什么做这个

市面上的磁盘工具大多分两类:一类只能"看",看完大文件还得自己一个个去处理;一类敢"动手",但让一个工具直接动我的文件,我是拒绝的------它凭什么保证不出事?

所以 Luma 的核心思路就一句话:**把"看清楚"和"动手"放进同一个工作区,中间隔着一层预览和确认,动手之后还能反悔。**

功能速览

扫描与空间分析

选一个目录递归扫描,只读元数据(名称、扩展名、大小、修改时间),不跟随符号链接,可以随时取消。单个文件没权限就计入错误数,不会中断整个任务。

扫完有分类分段条、Top 20 大文件、"值得留意"发现(超大文件、长期未动、开发构建内容、压缩包、安装包五类)。每一类都能下钻看明细,汇总数字和明细列表走的是同一套判定规则,不会出现"总数 32 个点开只有 25 个"的尴尬。

搜索:FTS5 + BM25 + 确定性条件

关键词走 SQLite FTS5 external-content 索引,BM25 排序,文件名权重高于路径。中文和特殊字符走安全的字面量匹配兼容路径------用户输入永远不会被当成 FTS 操作符。

比较有意思的是条件解析:

text 复制代码
"大于 1GB 的最近一年的 PDF" → size_bytes > 1GB AND mtime >= 一年前 AND extension = 'pdf'

注意这是纯规则解析,不是大模型------它把自然语言条件变成绑定参数和静态 SQL,可解释、可测试。解析器只认一组确定性模式(大于/小于 + 容量单位、最近一年、PDF、下载目录、代码项目),解析不出来的部分就当普通关键词,不会瞎猜。

重复文件:先比大小,再算哈希

先按文件大小筛出候选,再对候选算 BLAKE3 内容哈希,只对确认同内容的文件分组展示。可节省空间是按组去重后算的,不会把两个 10MB 重复文件算成 20MB。同样,只展示候选,不自动删。

规则整理:dry-run → 确认 → 可撤销

这是整个产品的核心链路。提供桌面 / 下载预设方案,也可以按类型、修改日期、扩展名、文件名关键词归类。流程是:

  1. 生成完整 dry-run 计划(预览阶段不碰文件系统)

  2. 预览清单:原路径 → 目标路径、归类依据,可以排除单个文件、改分类名,同名冲突选跳过或自动编号

  3. 确认后执行,成功项逐条写入 SQLite 操作日志

  4. 任何时候可撤销,撤销记录持久化,重启应用后依然有效

本地存储管理

v0.4.1 加的:Luma 自己的索引也占磁盘,所以应用里可以直接看索引占用、回收空闲空间、清理扫描索引。策略是只保留最近一次成功扫描的完整文件索引,历史扫描降级为统计摘要;单次扫描的索引写入有 256 MiB 预算,超限保留上次成功结果。一个管存储的工具,自己也得管好自己的存储。

工程结构

text 复制代码
前端 React 19 + TypeScript + Vite,useSyncExternalStore 轻量 store,i18next 双语

后端 Rust + Tauri 2

scanner.rs walkdir 遍历 + 分类

database.rs SQLite schema(v5)、FTS5、查询

search.rs SearchProvider 抽象 + 条件解析

duplicates.rs 大小预筛 + BLAKE3

file_ops.rs 文件操作 + 操作日志 + 撤销状态机

organizer.rs 规则整理 dry-run

cleanup.rs 清理建议

存储 rusqlite(bundled SQLite),FTS5 external content + 触发器同步

扫描在 Rust 阻塞任务里跑,文件按 500 条一批写入 SQLite,进度约 100ms 节流一次通过事件推给前端。前端不加载完整文件表,分页和聚合都在数据库做。

性能基线(本机 debug、10 万条合成记录,仅供参考不构成承诺):索引写入约 16.6s,关键词搜索约 4.5ms,数据库约 31 MiB。

踩坑实录(这部分最有价值)

坑 1:中文路径 + SQLite SUBSTR,字节数和字符数打架

目录树查询需要用"父路径长度"去 SUBSTR 截出相对路径。我最初用 Rust 的 str.len()------它返回字节数 ;而 SQLite 的 SUBSTR 按字符数截。ASCII 下两者相等,所以英文测试目录全绿。

换成中文路径就露馅了:/Users/me/项目/child/file.txt 应该截出 child/file.txt,实际截出了 d/file.txt。

修复很朴素:parent_path.chars().count(),然后补上中文、emoji、同前缀目录的边界测试。教训是:涉及路径的字符串运算,字节和字符必须全链路统一口径,测试夹具里必须有非 ASCII 路径。

坑 2:LIKE 前缀没转义 % 和 _

拼接 path LIKE ?/ 前缀时没转义通配符,虽然写了 ESCAPE '\',参数自己不带转义等于白写------目录 /work/a_c 的查询会匹配到 /work/abc。这不是注入,是查询语义错误,更隐蔽。修法是入库前统一 escape_like()。

坑 3:撤销中断后,记录永久卡死

撤销开始时会把操作状态写成 undoing。如果进程恰好死在这个窗口:启动恢复逻辑只处理 running,不认 undoing;再试撤销又撞上 OP_BUSY;可撤销列表也不显示它。一条记录就这么永久卡住了。

修复是加了 reconcile_interrupted_operations():启动时把 running 和 undoing 都标记为 interrupted,而撤销实现本身是逐项持久化的,所以中断的撤销可以安全续跑。配套写了一个"模拟 kill 在状态更新之后"的测试。教训:文件系统操作和 SQLite 事务不是原子的,任何状态机都要考虑"死在两个状态之间"。

坑 4:"可释放约 30MB" 的数学问题

清理建议面板最初把各类建议的体积直接相加:两个相同的 10MB 压缩包,"压缩包"算 20MB,"重复候选"再加 10MB,合计"可释放约 30MB"------实际文件只有 20MB,删掉一个副本最多省 10MB。

分类会重叠,估算不能直接求和。现在文案改成了"待复查文件约 N",口径收敛到扫描范围内的并集,语义从"承诺能省多少"退到"这些值得你看看"。工具类产品,文案的确定性不能超过证据。

坑 5:Windows 保护路径硬编码了 c:\

系统目录保护最初匹配 c:\windows 这类前缀------Windows 装在 D 盘的用户直接失去保护。改成从路径自身解析盘符再做盘符不敏感的匹配,加了专门测试。这类"在我机器上没问题"的平台 bug,只能靠把平台差异显式写进测试。

安装与更新

安装包尚未做平台代码签名和公证(macOS Developer ID + notarization 在计划里),所以首次打开会被 Gatekeeper 拦一次:

  • macOS:系统设置 → 隐私与安全性 → 「仍要打开」

  • Windows:SmartScreen → 「更多信息 → 仍要运行」

放行一次之后不会再拦。README 里有完整引导。

应用内自动更新走 Tauri updater,更新产物有独立签名校验(和平台签名是两回事),已经有过一次真实的跨版本升级验证(v0.3.2 → v0.4.0)。

明确不做的事

划边界和做功能一样重要:

  • 不做后台自动整理、不自动删除、没有永久删除(最多进废纸篓)

  • 不分析文档语义------分类依据是文件名、类型、修改时间这些可解释的规则

  • 暂不做 AI:等真实使用中出现"固定规则覆盖不了"的场景再说,来了也只做"把意图解析成结构化查询",不直接执行文件操作

最后

项目从 8 月开始写,最近刚发到 v0.4.1,质量门禁是前端测试 + Rust 测试 + fmt + clippy + 双平台 CI 构建全过。

Windows 真机验收还在完善,用出问题欢迎提 Issue;觉得有意思的话点个 Star,比什么都鼓励开发者 🌟

相关推荐
龙九山2 小时前
我的 10 个 npm 插件,差点被 AI 判了死刑
前端·开源
GitFun2 小时前
电脑空间总不够?实测120张挖出2.58MB重复
开源·工具
2601_949847752 小时前
开源鸿蒙平台 CMP 三方库「compose-remember-setting」适配全流程
华为·开源·harmonyos
resh_people2 小时前
开源鸿蒙平台 CMP 三方库「snowfall-compose」适配全流程
华为·开源·harmonyos
进阶的猪2 小时前
OpenHarmony Native应用在Linux环境下交叉编译流程详解:CMake、Clang与链接过程
开源·ohos
云南新人和数采集团3 小时前
开源项目吐槽大会:一篇技术文章的大纲设计
python·scrapy·开源
2501_944525543 小时前
开源鸿蒙平台 KMP 三方库「KSafe」适配全流程
华为·开源·harmonyos
老王IT3 小时前
Rust开发环境搭建教程
开发语言·后端·rust