DiskScope:找出是谁在悄悄吃掉你的硬盘

一个 Windows 磁盘分析 + 清理工具。扫 16 万个文件只要 1.7 秒, 并且会告诉你谁在长, 而不只是谁大。


起因

C 盘又满了。

每次都得开个现成的工具, 然后等它慢慢扫, 看进度条慢慢转, 扫完再自己对着几百兆的 node_modules 猜哪些能删。

这些工具做的是快照: 告诉你此刻谁占地方。但我真正想知道的是另一件事 ——

这个目录是什么时候开始变大的? 是不是某个东西在偷偷长?

Steam 的 workshop 素材会自动重新下载并换目录 ID, 某些项目隔三差五 npm install 一次, 缓存目录悄悄膨胀到几十 GB —— 这些在静态快照里完全看不出来。

所以我写了 DiskScope,全程vibecoding将想法变成现实。

image.png

image.png

image.png

image.png

四个页面

1. 体积分布

Squarified treemap, 点任意方块下钻, 配可展开的层级明细。目标是一眼看出空间去哪了。

2. 时间轴(核心)

最近 30 天每天写入了多少数据, 堆叠柱状图, 外加一张「谁在悄悄变大」排行榜。

这是这个工具和别的磁盘分析器最不一样的地方:

▼
text
复制代码
今天写入 578 MB │ 最近 7 天 1.8 GB │ 最近 30 天 5.4 GB │ 30 天以上没动过 142 GB

一眼就能分清: 上面那 5.4 GB 是活的东西, 下面那 142 GB 是死的。后者才是清理的目标。

3. 清理

53 条规则, 分三级:

级别数量含义例子
🟢 可重建39删了会自动重新生成, 默认勾选node_modules target dist __pycache__
🟡 需确认12要重新下载, 或会丢历史记录, 默认不勾.venv Pods *.log .idea
🔴 勿删2勾选框直接禁用.git *.tfstate

配套 dry-run 预演: 先看清要动什么, 再决定删不删。

4. 大文件

面向普通文件夹(照片 / 视频 / 安装包 / 归档)。构建垃圾规则匹配不到这些, 所以单独追踪: Top 200 大文件 + 按扩展名汇总。


技术上花了力气的地方

16 个 worker 线程, 单线程的 5.5 倍

第一版用 fs.promises.stat + 异步池, 16 万文件要 8.4 秒。瓶颈不是 Promise 开销, 是 stat 系统调用本身。

改成 worker 线程池后, 主进程只管派活, worker 各扫各的:

worker 数文件/秒相对单线程
120,8441.0x
463,3433.0x
890,9234.4x
16114,0885.5x

实际效果: 扫完 161,207 个文件 / 70 GB, 1.7 秒。

31 格直方图, 任意层级 O(1) 回答「最近长了多少」

每个目录存一个 31 格的直方图(第 0 格 = 今天, 最后一格 = 30 天前的冷数据), 自底向上滚动相加:

▼
js
复制代码
// 父目录的直方图 = 自己的 + 所有子目录的 for (let i = 0; i < NDAYS; i++) daily[i] += child.daily[i];

于是任意层级都能 O(1) 回答"这里最近 7 天长了多少", 不需要重新遍历子树。

有界小顶堆, 内存恒定

要记住最大的 200 个文件, 但不能随扫描量涨内存。堆顶始终是 Top-N 里最小的那个, 新文件比它还小就直接丢弃:

▼
js
复制代码
push(v) { if (a.length < cap) { a.push(v); this._up(a.length - 1); } else if (v[0] > a[0][0]) { a[0] = v; this._down(0); } // 堆顶换掉 }

扫 5 万个文件和扫 500 万个文件, 内存都是 200 条。


踩过的坑(这部分可能比功能更有用)

1. Electron 的 asar 补丁会悄悄改掉你的统计结果

Electron 会把「路径最后一个扩展名是 asar 的文件」当成 asar 包来解析:

  • 合法的 asar → statSync 返回 0 字节(把包根当虚拟目录)
  • 损坏的 asar → 直接抛 Invalid package

结果就是所有 .asar 文件的大小都没被统计, 磁盘总量一直偏小, 而且 stderr 刷一片错误。

绕法是加 NTFS 备用数据流后缀 ::$DATA(指向文件本体), 路径最后一个扩展名变成 DATA, Electron 就不会去解析 asar 了:

▼
js
复制代码
function statFile(p) { if (/\.asar$/i.test(p)) { try { return fs.statSync(p + "::$DATA"); } catch { /* 退回普通方式 */ } } return fs.statSync(p); }

2. D:\ 自带尾随分隔符, 拼路径会多一个反斜杠

▼
js
复制代码
path.resolve("D:\\") // "D:\\" ← 带尾随分隔符 dirPath + "\\" + fn // "D:\\software" ← 双反斜杠 path.join(dirPath, fn) // "D:\software" ✓

Windows 内部会容忍双斜杠, 但路径字符串比较会失效 —— 我那个「文件被挪走了, 帮你找回来」的功能就是这么失灵的。改用 path.join 一劳永逸。

3. 悬停提示会残留在页面上

提示框原本是每次 hover 新建一个 div 挂在 document.body 上, 引用存在柱子元素里。点击柱子下钻 → 图表整个重建 → 旧元素被丢弃 → mouseleave 永远不触发 → 提示框永久留在屏幕上。

修法是改成全局单个复用的节点, 从根上消除这类孤儿节点。

4. 文件会「被挪到隔壁去」

Steam 重新下载 workshop 素材时会换一个新的目录 ID, 文件名却不变:

▼
text
复制代码
扫描时: ...\431960\344551134\CSCT-013.mp4 现在: ...\431960\3445511134\CSCT-013.mp4 ← 多了一个 1

于是点路径时文件「不存在了」。现在的处理是分三档:

  1. 目标在 → 正常定位
  2. 被挪到隔壁去了 → 按文件名在上一级的其他子目录里找一遍, 找到就直接定位到新位置
  3. 真没了 → 打开最近还存在的上级目录, 并明确告诉你

三种情况都会给提示, 不会静默无反应。


安全设计

清理工具删的是用户的数据, 所以默认必须是保守的:

  • 默认进回收站(可还原), 而不是直接 rm
  • dry-run 预演先过一遍
  • 三级分类, .git 永远不可勾选
  • 服务端硬性闸门: 磁盘根目录、工具自身所在目录和程序文件, 一律拒绝
  • 嵌套路径自动去重(选了 node_modules 就不会再单独处理它内部的 .vite)
  • Electron 版把 Python 版那套「本地 HTTP + 随机 token」整个去掉了 —— 改成 IPC, 连端口都不占

测试

118 项检查, 打包版也跑:

▼
text
复制代码
规则校验 27 项 │ 后端 40 项 │ 界面 51 项

界面自检是真的去点按钮的 —— 包括点「停止」并验证扫描真的停下来了, 点路径并验证资源管理器真的打开了。


获取

绿色版(免安装, 单文件):

▼
text
复制代码
dist\DiskScope-1.0.0-绿色版.exe

从源码构建:

▼
bash
复制代码
npm install npm run build # electron-builder + 写入图标与版本信息 npm run uitest # 界面自检, 会自动截图

Python 版(只用标准库, 双击 run.bat 就能跑)也保留着, 两版共用同一套前端和同一份规则表。

源码: https://github.com/star-sda/diskscope


最后

工具的开发全程vibecoding,作为一名javaer真切感受到了AI的强大。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP