SpaceLens-盘镜:基于 Rust 生态的跨平台磁盘空间分析与清理项目

定位:Rust 编写的跨平台磁盘空间分析、可视化、清理工具。 双端:ratatui TUI + egui 桌面 GUI。 核心目标:WizTree 级扫描速度、Cleaner 级清理体验、扫描时 UI 不卡。|作者个人站:https://softwarescience.top/projects/spacelens

分支1Tags1

SpaceLens / 盘镜

作者个人站:https://softwarescience.top/ · 本项目主页:https://softwarescience.top/projects/spacelens(版本、截图与下载都在那儿)

Rust 编写的跨平台磁盘空间分析、可视化与清理工具。终端界面(ratatui TUI)、桌面界面(egui GUI)与命令行(clap CLI)共用同一套扫描引擎与不可变快照。

扫描走快照、删除走队列、UI 永远只读不可变快照,所以扫描时界面不会卡。


1. 这是什么

盘镜回答三个问题:

  1. 空间去哪了:按目录逐层下钻,看大小、占比、修改时间;矩形树图一眼看出「谁最大」。
  2. 什么可以删:重复文件检测、大文件视图、按类型统计、规则引擎给出的清理建议(带风险等级)。
  3. 删了会不会出事:删除前先出计划(多少项、多少 GB、哪些被拦截),默认进回收站,永久删除必须显式确认。

三种入口,一套内核:

入口 命令 适合
终端界面 spacelens tui [路径] 服务器、SSH、不想离开终端
桌面界面 spacelens gui [路径] 日常清理、看矩形树图
命令行 spacelens scan/dup/clean/... 脚本、批处理、CI

2. 特性

功能 状态 说明
并行目录遍历扫描(jwalk + rayon) ✅ 跨平台默认路径,进度实时上报,可取消
Windows NTFS MFT 快速扫描 ✅ 代码完整 / ⚠️ 需管理员 直接解析 $MFT,allocated 为精确分配大小;非管理员自动降级并在界面标注原因
不可变快照 + 无锁读取 ✅ 扫描线程与 UI 完全解耦,ArcSwap 发布
文件夹占用排行 / 大文件视图 ✅ 按大小、名称、占用、时间、类型、子项数排序
矩形树图(squarified treemap) ✅ TUI 与 GUI 共用同一份布局算法
过滤表达式 ✅ size>1G ext:mp4 older:30d dir 名称关键词
重复文件检测 ✅ 大小分组 → 首尾 64 KiB 快速哈希 → 全量 SHA-256 三级过滤
按类型统计 ✅ 视频/图片/音频/压缩包/程序/源代码/文档/临时…
清理规则引擎 ✅ TOML 规则 + glob 匹配 + 风险等级(低/中/高);内置「开发缓存」「系统临时文件」「大视频」等规则
删除计划与安全拦截 ✅ 去重、受保护路径、卷根/扫描根拦截、dry-run、永久删除二次确认
回收站删除 ✅ 默认方式,走系统回收站(Windows 为 Shell IFileOperation)
快照保存 / 载入 / 对比 ✅ redb 持久化,对比报告含新增/删除/增长/缩小与净变化
导出 CSV / JSON ✅ 命令行与界面都可导出
卷容量总览 ✅ 各盘已用/可用/使用率、介质类型
中文排版适配 ✅ 三端按显示宽度对齐(中文算 2 列),内置字体回退,视觉模型核验脚本
增量扫描 ✅ 只重扫变更子树(目录 mtime 判定),不可增量时自动退化为全量(scan --incremental)
文件系统监视 ✅ 目录变动即时增量更新(TUI/GUI 扫描完成后自动开启)
远程扫描(SSH) ✅ 远端跑同款扫描器,二进制快照流回本地(scan --remote user@host:/path)
回收站「撤销」 ✅ 按 $I/$R/.trashinfo 元数据映射还原(spacelens undo;TUI 快捷键 U)
插件式规则(脚本动作) ✅ 规则可挂外部命令:预演 → 显式确认 → 超时强杀,不经 shell

3. 快速开始

3.1 构建

cargo build --release

产物:target/release/spacelens.exe(Windows)/ target/release/spacelens。

3.2 三种入口

spacelens tui  D:\              # 终端界面
spacelens gui  D:\              # 桌面界面
spacelens scan D:\ --top 30     # 命令行分析

3.3 想先拿假数据试试

python tools/make_demo_tree.py            # 生成 .spacelens-dev/demo(含中文目录名、缓存目录、重复文件)
spacelens scan .spacelens-dev/demo --top 20
spacelens dup  .spacelens-dev/demo --min-size 100K
spacelens tui  .spacelens-dev/demo

WizTree 之外的真实场景:直接 spacelens scan D:\(管理员身份可走 NTFS MFT 快速路径)。


4. 命令行用法

# 扫描
spacelens scan D:\ --top 30 --top-files 20          # 目录排行 + 全树最大文件
spacelens scan D:\ --filter "ext:mp4 size>1G"       # 过滤表达式
spacelens scan D:\ --exclude "**/node_modules" --json
spacelens scan D:\ --save 首次快照                   # 扫描并保存快照
spacelens scan D:\ --no-mft --threads 8             # 关闭 MFT、限制线程

# 重复文件
spacelens dup D:\ --min-size 10M --quick

# 清理(默认只预演,绝不动磁盘)
spacelens clean D:\下载 --filter "ext:tmp"                 # 预演
spacelens clean D:\下载 --filter "ext:tmp" --trash --yes    # 进回收站
spacelens clean . --rule "开发缓存" --permanent --yes       # 按规则永久删除(会二次确认)
spacelens clean . --rule "导出清理" --run-scripts --yes     # 执行脚本动作规则(高风险:--run-scripts + --yes 双开关)

# 撤销还原(默认只预演;--yes 才动磁盘)
spacelens undo                          # 预演最近一条可撤销删除
spacelens undo --last 3                 # 预演最近 3 条
spacelens undo --id 5 --yes             # 还原删除日志 #5

# 增量扫描(只扫变更目录)与远程扫描(远端需装 SpaceLens)
spacelens scan D:\ --incremental
spacelens scan --remote user@server:/var/data --top 20

# 快照
spacelens snapshot save 周一 D:\
spacelens snapshot list
spacelens snapshot compare 1 2 --top 30
spacelens snapshot export 2 --format csv --out today.csv

# 其它
spacelens volumes                       # 各盘容量
spacelens rules list                    # 当前生效规则
spacelens rules init                    # 导出规则模板
spacelens config init && spacelens config path
spacelens export D:\ --format json --out full.json --files-only

全局开关:--config <文件>、--cache <文件>、--json、--no-cache、--quiet。

4.1 退出码

码 含义
0 成功
1 出错(扫描失败、参数非法、计划被拒绝等),错误信息以 错误: 开头

5. 界面

5.1 TUI 按键

键 功能
Enter 进入目录 / 查看
Backspace u 返回上级
↑ ↓ PgUp PgDn Home End 移动游标
Space 勾选 / 取消
a / c 全选 / 清空选择
s 排序菜单(键 + 方向)
t 切换视图:列表 / 矩形树图 / 大文件 / 重复文件 / 类型统计 / 清理建议
/ 过滤表达式输入
: 命令模式(:scan <路径>、:filter size>1G、:delete selected --trash、:dup、:snapshot save <名称>)
d 删除到回收站(需确认)
D 永久删除(需输入 yes)
U 撤销最近一次删除(从回收站还原)
r 重新扫描
? 帮助
Esc 关闭浮层 / 取消扫描
q / Ctrl+C 退出

5.2 GUI 布局

┌──────────────────────────────────────────────────────────────┐
│ 工具栏:路径输入 · 扫描 · 取消 · 视图切换 · 删除 · 规则 · 快照 │
├────────────┬─────────────────────────────────┬───────────────┤
│ 卷与目录树 │ 列表 / 矩形树图 / 大文件 /      │ 详情与操作    │
│            │ 重复文件 / 类型统计 / 清理建议  │               │
├────────────┴─────────────────────────────────┴───────────────┤
│ 状态栏:进度 · 速度 · 当前路径 · 已选 N 项 · 可释放 X GiB     │
└──────────────────────────────────────────────────────────────┘
  • 中文字体自动从系统加载(微软雅黑等);找不到时界面顶部会显示红色提示,而不是渲染成方块。
  • 长路径在单元格内截断(不换行),行高固定,中文不会被裁掉。
  • 删除确认弹窗会列出计划摘要与被拦截项,永久删除需要额外勾选。

6. 配置与规则

配置文件默认位于 %APPDATA%\SpaceLens\config.toml(其他平台 $XDG_CONFIG_HOME/spacelens/config.toml)。

[scan]
follow_links = false      # 是否跟随符号链接
include_hidden = true     # 是否统计隐藏文件
prefer_mft = true         # Windows 优先尝试 NTFS MFT(需要管理员)
exclusions = ["**/node_modules", "**/.git"]

[delete]
default_mode = "trash"    # trash | permanent
require_confirmation = true
protected_paths = ["C:\\Windows", "C:\\Program Files"]   # 这些路径及其子树禁止删除

[rules]
enabled = true
path = "C:\\Users\\me\\AppData\\Roaming\\SpaceLens\\rules.toml"

[ui]
language = "zh-CN"
page_size = 200

规则文件(spacelens rules init 生成模板):

[[rules]]
name = "开发缓存"
match = ["**/node_modules", "**/target", "**/.venv", "**/__pycache__"]
action = "trash"
risk = "low"

[[rules]]
name = "大视频"
match_ext = ["mp4", "mkv", "mov"]
min_size = "1GB"
action = "mark"
risk = "medium"

# 脚本动作:命中后执行外部命令(高风险,需 --run-scripts + --yes 双开关才执行)
# 占位符:{path} {name} {size} {ext};命令与参数逐段传递,不经 shell
[[rules]]
name = "导出清理"
match = ["**/导出/*.tmp"]
action = "script"

[rules.script]
command = "python"
args = ["cleanup.py", "{path}", "{size}"]

[[rules]]
name = "冷日志"
match_ext = ["log"]
older_than_days = 30
action = "trash"
risk = "low"
  • action:trash(建议回收站)/ permanent(建议永久删除)/ ignore(永不建议)/ mark(只标记)。
  • risk:low / medium / high,界面按等级着色,高风险默认不勾选。
  • 单位语义:KB = 1000、KiB = 1024(G/GB 与 GiB 同理)。

7. 架构

前端  ratatui TUI ─┐
      egui GUI    ─┼─ Command(下发) / CoreEvent(上报)
      clap CLI    ─┘            │
                                 ▼
                       spacelens-core 引擎
        ┌──────────────────────────┼──────────────────────────┐
   扫描调度线程                工作线程池                 快照发布
   (命令总线)            扫描 / 删除 / 哈希        ArcSwap<Snapshot>
        │                        │                          │
   spacelens-mft          spacelens-delete          spacelens-cache
   (NTFS $MFT)          (回收站 / 永久)          (redb 快照与日志)
                                 │
                          spacelens-rules(TOML 规则)
crate 职责
spacelens-core 数据模型(Arena + NodeId)、扫描、删除、重复检测、快照对比、格式化、引擎
spacelens-rules TOML 规则解析与匹配、parse_size
spacelens-cache redb 持久化:快照、删除日志、KV
spacelens-mft NTFS $MFT 解析(BPB、FILE 记录、USA 修复、属性遍历)
spacelens-ui-common 双端共享 UI 状态:过滤表达式、排序、选择、treemap 布局
spacelens-tui ratatui 终端界面
spacelens-gui egui 桌面界面(glow 后端)
spacelens-cli 可执行文件 spacelens

7.1 四条不变式

  1. UI 线程不做 IO:扫描/删除/哈希都在工作线程,UI 只读快照与事件。
  2. 快照发布后只读:修改 = 克隆出新快照再发布,读侧无锁。
  3. 危险操作先成计划:删除必经 plan()(去重 + 拦截 + 确认位)再 execute()。
  4. 目录大小恒为子树汇总:由引擎统一计算,保证一致且可重算。

7.2 数据结构

type NodeId = u32;                    // Arena 下标,替代指针
struct FileNode { parent, children, name: Box<str>, kind, size, allocated, mtime }
struct Snapshot   { nodes: Vec<FileNode>, root, root_path, source, totals, version, ... }

百万级节点的内存占用主要是 Vec<FileNode>(每节点约 72 字节)加上名字字符串,节点不存完整路径,路径按需拼装。快照用自定义二进制格式(magic SLSN + 版本 + 校验),解码后会重算统计值与记录值比对,损坏数据直接报错而不是静默出错。


8. 性能与验证

本机实测(Ryzen 9 9900X / 24 线程 / Windows 11 26200 / release 构建):

场景 结果 命令
C:\Windows\System32(24,565 文件 / 1,562 目录 / 10.76 GiB) 2.5 s(≈ 10k 项/秒,冷缓存首轮 3.4 s) cargo run --release -p spacelens-core --example bench_scan -- "C:\Windows\System32"
本仓库 target(12,687 文件 / 1,122 目录) 1.0 s(≈ 13k 项/秒) 同上
小目录(41 项) 6 ms 同上

说明:

  • 并行遍历路径在 Windows 上每个条目需要一次元数据调用,吞吐量约 10–14k 项/秒;百万文件约 70–100 s,与设计目标(< 60 s)同量级。
  • NTFS MFT 快速路径目标为「百万文件 < 10 s」:解析器有合成镜像单测覆盖,但需要管理员权限,本机(非管理员)只验证了自动降级路径。
  • Windows 并行遍历的 allocated 按 4 KiB 簇向上取整估算(Metadata 不提供分配大小);MFT 路径给出精确值。
  • allocated 在 macOS/Linux 上由 st_blocks × 512 得出,精确。

8.1 测试

cargo test                  # 全量:246 个测试(单元 + 端到端 + 无头渲染),全绿
cargo test -p spacelens-core --test e2e     # 真实文件系统端到端
cargo clippy --workspace --all-targets      # 0 warning

各 crate 的测试分布:

crate 覆盖
spacelens-core 40 单元 + 11 真实文件系统端到端
spacelens-mft 34(合成 NTFS 镜像、USA 修复、DOS 短名、扩展记录、BAAD 记录)
spacelens-rules 38 + 2 外部 API 契约
spacelens-cache 15 + 2 外部 API 契约
spacelens-ui-common 25(CJK 宽度、过滤表达式、treemap 几何、选择去重)
spacelens-tui 30 单元 + 7 无头整帧渲染
spacelens-gui 20 单元 + 2 二进制入口
spacelens-cli 4 单元 + 12 真实二进制端到端

覆盖要点:

  • 核心端到端:在临时目录树(含中文目录名/文件名)上扫描,断言文件数、目录数、总字节数、路径重建、排序、排除规则、取消、MFT 降级、删除计划与执行、受保护路径拦截、快照存/取/对比、重复文件、异步事件顺序。
  • CLI 端到端:运行真实二进制,断言中文表头、JSON 字段、CSV 行数与内容、预演不落盘、永久删除需要 --yes、无过滤条件时拒绝清理、快照全流程。
  • TUI:用 ratatui TestBackend 无头渲染整帧(120×40 / 60×24 / 44×14 / 20×5 等尺寸、6 种视图),断言「每行显示宽度不超过帧宽」「不出现替换字符」以及关键中文标签存在。
  • GUI:字体探测(真实系统字体签名)、表格行模型、treemap 命中测试、删除确认门的勾选要求。
  • 解析器:NTFS 合成镜像逐字段断言,且全部无需管理员权限。

8.2 截图核验(已执行)

TUI 与 GUI 都在真实窗口里跑过并逐像素确认:

检查项 结果
中文显示 正常,无方块/乱码(GUI 自动加载系统字体:msyh.ttc 等)
列对齐 列表的 名称/大小/占用/修改时间/类型 按显示宽度对齐(中文算 2 列)
截断 无越界、无压住相邻元素;长路径在面板内换行且不破坏边框
发现的真实缺陷 GUI「修改时间」列宽 116 px 放不下 2026-09-23 23:28 被截断 → 已改为 150 px

自动化工具(tools/ui_verify.py)支持按窗口标题截图(PrintWindow,被遮挡也能抓;GL 窗口抓到空白时自动回退整屏裁剪)、注入鼠标点击、以及把截图交给本地视觉模型逐条检查排版清单。核验截图存在 screenshots/(未纳入版本控制)。


9. 安全说明

磁盘清理工具最怕的就是「手一抖删错」。盘镜的策略:

  1. 默认不删:clean 不带 --yes 只预演;TUI/GUI 的删除也要确认。
  2. 默认回收站:永久删除需要显式 --permanent 且 --yes(界面上还要额外勾选)。
  3. 受保护路径:C:\Windows、Program Files、ProgramData、~/.ssh、~/.gnupg 等及其子树禁止删除;卷根与扫描根本身禁止删除。路径比较对大小写、正反斜杠、\\?\ 扩展前缀免疫,无法用不同写法绕过。
  4. 去重:勾了目录又勾它里面的文件时,只删目录一次,可释放空间不重复计算。
  5. 可审计:删除日志(路径、大小、方式、成功与否)写入 cache.redb。
  6. 无过滤不清理:clean 未指定 --filter/--rule 时直接拒绝执行,避免误删整个目录。

10. 路线图


11. 开发

cargo test                       # 全量测试
cargo run -p spacelens-cli -- scan . --top 10
cargo run --release -p spacelens-core --example bench_scan -- D:\ 3

工程约定、依赖踩坑记录、中文排版规范见 AGENTS.md。


12. 变更记录

未发布

  • 修复(实机验证发现):NTFS $MFT 多 run(物理不连续)时按线性偏移读取会把后段记录读成别的数据,导致条目大面积缺失、「MFT 中不存在路径」后降级 Walk。现按记录 0 $DATA 的 mapping pairs(runlist)把流内偏移映射到卷上偏移,并补碎片化/短读回归测试与真实卷实机测试(tests/real_volume.rs)。
  • 新增增量扫描、文件系统监视、远程扫描(SSH)、回收站撤销、插件式规则(脚本动作),对应 CLI 子命令与 TUI 快捷键见上文。
  • 界面修复:底部扫描进度不再与右下角「已选」摘要重叠(左右分栏、长路径截断);「取消」现在会一并停止扫描与自动更新,取消后界面立即恢复可扫描状态(此前会卡在「扫描中」)。
  • 界面增强:表格与目录树增加「占比」(相对当前目录);矩形树图改为 wintree 式热点色阶(冷→热五档 + 左下角图例),格子标签带占比;目录树的展开三角与「进入目录」动作分离(点三角只展开/收起,点目录名才进入)。

v0.1.0

  • 首个可用版本:核心引擎、并行扫描、NTFS MFT 解析、TUI、GUI、CLI、规则引擎、快照缓存、重复文件检测、删除计划与安全拦截。

13. 许可

MIT,见 LICENSE。

关于

这个项目由 CodeLife Leon 独立开发与维护。更多项目、各项目发版信息、赞赏与联系方式都在个人站:

问题反馈与需求建议:优先在仓库提 Issue,也可以邮件 virgil_lgp@163.com。

项目介绍

定位:Rust 编写的跨平台磁盘空间分析、可视化、清理工具。 双端:ratatui TUI + egui 桌面 GUI。 核心目标:WizTree 级扫描速度、Cleaner 级清理体验、扫描时 UI 不卡。|作者个人站:https://softwarescience.top/projects/spacelens

https://softwarescience.top/projects/spacelens定制我的领域