
这个工具解决什么问题
硬盘红了。手机导一遍、微信自动存一遍、做备份又拷一遍,同一张照片在「相册」「微信导出」「备份」三个目录里各有一份;一个设计稿因为反复导出,留下了 logo.png、logo_v1.png、logo_v2.png、客户确认/最终版/logo.png 四个文件。想清一清,打开文件管理器按大小排个序,结果发现两个完全不同的安装包体积一模一样;想按文件名猜,又怕把「看起来像同一张但其实是修过的另一版」删掉。于是要么一个个打开图片肉眼比对——几百张看下来眼都花了,看到一半就开始凭感觉点;要么干脆放弃,让硬盘一直红着。很多人最后是用「整体删掉某个备份目录」这种粗暴办法腾空间,赌的就是「应该没删掉重要的东西」。
- 原来的做法:在文件管理器里按体积排序,逐个打开比对;或者靠文件名猜(_副本、_1、IMG_xxxx 完全不可信);再不行就整个备份目录一起删,赌一把
- 用这个工具:点「添加文件夹」选位置,点一次「开始扫描」,程序先按体积筛、再比对文件头尾、最后算完整内容摘要,把内容真正一致的文件分成一组列出来;每组自动建议保留一份,其余勾上,点「移到回收站」就清完了
- 省下来的:把「几百个文件看到眼花、还不敢下手」变成一次点击;而且清理只走系统回收站,删错了随时右键还原——不用再靠运气
技术实现
运行期只用 Python 标准库,打包后是单个 exe,双击即用,不联网、不需要装任何东西。核心是三件事:① 找重复 —— 采用「按体积分组 → 头尾分片哈希 → 全文件 SHA-256」三层收敛。体积唯一的文件不可能重复,直接淘汰;同体积的再读头尾各 64KB 做廉价指纹;只有挺过前两关的才被完整读取。② 敢下手 —— 清理动作只走 Windows 系统回收站(shell32 的 SHFileOperationW + FOF_ALLOWUNDO),界面上刻意不提供「永久删除」,删错了打开回收站右键就能还原。③ 不假死 —— 扫描跑在后台线程里,只用队列往主线程送进度与结果,几万个文件的扫描过程中界面照样能点、能停。结果按「可释放空间」降序排列,大文件先出现在最上面,用户最先看到的就是最值得处理的那些。
- 三层收敛:先比体积,再比头尾,最后才算全量摘要:如果老老实实把每个文件都完整读一遍算 SHA-256,一块装了几十万文件的硬盘要读很久,而其中绝大多数文件其实一眼就能排除。程序分三步收窄:第 1 步按文件大小分组,体积唯一的直接淘汰(体积在枚举阶段就已经拿到,这一步几乎不花钱);第 2 步只读同体积文件的头部 64KB 加尾部 64KB 算一个廉价指纹,把「大小一样但内容不同」的文件挡掉——这一类的数量比想象中多得多,比如同一批导出的安装包、同一台相机不同设置拍出的照片;第 3 步才把剩下的文件按 1MB 分块完整读取算 SHA-256。程序里记录了每一层实际读了多少字节,可以直接看到第 2 步省下了多少读取量。
- 头尾都要读,而且把文件长度拌进摘要:只读文件头是不够的:很多格式的头部是固定魔数,两个不同文件的前 64KB 完全可能一样,区别全在后半段。所以头尾各取一段。另外摘要在计算时额外混入了「文件长度」这一项——不这么做的话,理论上存在「头尾相同、中间不同、而且长度也不一样」的两个文件产生相同指纹的可能,虽然概率极低,但这正是那种「平时看不出来、出一次就是数据事故」的地方。第 3 步的全文件 SHA-256 才是唯一判定依据,前两步只负责淘汰,绝不参与下结论。
- 判定只认内容,但把「文件名不同」的情况专门留住:很多同类工具是按文件名分组,同一份照片存成 海边日落.jpg 和 IMG_2031.jpg 就找不出来,而这恰恰是最常见的场景(手机导出改名、微信自动重命名)。本工具全程只用内容做判定,文件名完全不参与:程序内部按「体积 + 内容摘要」建索引,所以跨目录、跨文件名、跨层级的同一份文件会被正确地归到同一组。反过来,两个文件即使名字一模一样、体积也一模一样,只要内容有一个字节的差别,就不会被归到一起——绝不靠猜。
- 清理只走系统回收站,界面上没有「永久删除」:这是刻意的设计取舍,不是功能缺失。清理重复文件恰恰是最容易误删的场景:两组文件同名同大小只是内容不同、或者用户手滑把保留项勾上了。程序的唯一清理动作是调 Windows 的 SHFileOperationW 把文件送进回收站(带 FOF_ALLOWUNDO 标志),用户发现删错了,打开回收站右键「还原」即可。顺带一提,「全选本组」这个按钮不会勾上自动建议保留的那一份——那等于把整组删光,属于必须挡住的操作。
- 界面永不假死:后台线程只往队列里放消息:tkinter 不是线程安全的,从非主线程碰界面对象会抛「main thread is not in main loop」,或者更难查的随机崩溃。所以扫描跑在独立的线程里,它只做一件事:把进度和结果塞进一个队列;主线程每 80 毫秒把队列里的消息搬进界面。好处是几十万个文件的扫描过程中,窗口照样能拖动、能滚动,「停止」按钮点下去两三秒内必定生效——因为取消标志每一轮循环都会检查一次。这也是「扫到一半发现选错目录了」不至于要杀掉进程的原因。
- 只跳过该跳过的目录,其余一个都不漏:程序默认跳过回收站、Windows、WinSxS、node_modules、.git、__pycache__、Temp 这类目录——它们里面的文件要么是系统自己管的,要么是缓存,清理它们没有意义还容易把系统搞坏。跳过规则只认目录名,不认路径片段,所以你把项目放在 D:\work\node_modules_test 这样的目录下也不会被误跳过。另外枚举文件用的是 os.scandir 而不是 os.walk:在 Windows 上 scandir 能直接拿到目录项里已经缓存的体积和修改时间,省掉一次系统调用,几十万文件的场景下差别很明显。
技术栈:Python 3.8+、tkinter 图形界面、纯标准库(运行期零第三方依赖)、SHA-256 内容比对(分块流式读取)、Windows 回收站 API(SHFileOperationW)、PyInstaller 单文件打包(--noconsole)
已知局限:只找内容完全一致的文件,不做相似图片识别——同一张照片的不同分辨率版本、加了水印的版本、或者只改了一个像素的版本,都不会被判为重复(这类需求需要感知哈希,属于另一个量级的功能)。默认跳过 0 字节文件:它们内容当然相同,但删了不省空间,混进结果里只会让汇总数字看着像算错了。扫描总耗时取决于硬盘类型(机械盘与固态盘差好几倍)和文件数量,视机器而定,不是固定值。程序不会自动清理任何东西,所有勾选和确认都由你来做。
操作教程
- 解压后双击打开
解压压缩包,双击「重复文件查找清理.exe」。不需要安装 Python 或任何运行库,程序也不联网。
预期结果:出现主窗口:上面是扫描设置,中间左边是重复组列表、右边是文件明细,底部是汇总与操作按钮 - 添加要扫描的文件夹
点「添加文件夹…」,在弹出的系统对话框里选一个目录,可以重复点、添加多个(比如同时加上「照片」和「下载」两个盘上的目录),也可以点「移除所选」把加错的去掉。
预期结果:文件夹清单里逐行出现你选的路径;同一个目录添加两次会被提示「已经在列表里」而不会重复计入 - 看一眼扫描选项(默认值就能用)
「最小文件大小」默认 1 KB,把一堆零碎小文件挡在外面;「包含子文件夹」默认勾上;「跳过隐藏文件与系统目录」默认勾上,避免去动系统文件;如果只想处理某一类文件,在「只看这些类型」里填 jpg,mp4 这样即可,留空表示全部文件都看。
预期结果:选项旁边有一行小字说明格式写法;填了不认识的扩展名也不要紧,只是筛不出文件而已,不会报错 - 点「▶ 开始扫描」
程序先在后台枚举文件(这一步显示「已扫描 N 个目录、找到 M 个文件」),然后进入比对阶段。进度条会随着比对推进,中途随时可以点「■ 停止」。
预期结果:状态栏持续显示当前阶段与进度;窗口全程可以拖动、滚动,不会出现「未响应」的白屏 - 看结果,确认哪一份留下
左边是重复组总览,每组显示份数、单份大小、这一组清掉后能释放多少空间,以及自动建议保留的文件名。点某一行,右边列出这组里的全部文件:方框打勾的是「待清理」,绿色那一行是建议保留的那一份。想改就点最左边的方框手动切换,也可以用「全选本组 / 全不选本组 / 反选本组」。底部的「保留策略」下拉框可以先选好(保留最早的、最新的、路径最短的等),再点一次就按新策略重新标记。
预期结果:重复组按可释放空间从大到小排列,最大的一组在最上面;勾选后底部汇总立刻更新「已勾选几个、可释放多少」 - 点「移到回收站(可还原)」
程序会先告诉你「即将把 N 个文件移到回收站,可释放约 X」,确认后执行。这一步是唯一的清理动作,文件进入 Windows 回收站,不会直接消失。
预期结果:弹出完成提示,写明移入了几个、有没有失败项;列表里已清理的文件自动消失,不足两份的组也随之消失 - 需要留档就导出清单,删错了就去回收站还原
点「导出清单 CSV」保存一份记录(带 BOM,Excel 双击打开中文不乱码),可以拿它对照检查。如果发现删错了:打开「回收站」→ 选中文件 → 右键「还原」,文件会回到原来的位置。
预期结果:CSV 每组列出文件名、体积、修改时间、完整路径与状态(保留/待清理);清空的回收站才会真正释放空间
常见问题
技术实现与后续维护
技术构成:Python 3.8+ 纯标准库,界面用 tkinter,摘要用 hashlib,回收站接口用 ctypes 直接调 Windows 的 shell32。运行环境:Windows 10/11 64 位,打包成单文件 exe 后不需要安装 Python;程序不联网、不依赖任何第三方包。维护要点:① 所有逻辑集中在 dup_core.py,界面只是它的消费者,改动判定逻辑不需要动界面;② 改动哈希判定相关代码后必须重跑自测的哈希分组(那里把「长度参与摘要」这条规则写成了显式断言,防止日后被「顺手简化」);③ 清理动作没有、也不应该添加「永久删除」这一分支——用户数据安全优先于操作便利;④ 界面层的铁律:后台线程只能往队列里放消息,绝不能在非主线程里碰任何界面对象,这是 tkinter 最容易出随机崩溃的地方。
技术构成:
dup_core.py—— 核心逻辑层:目录枚举与过滤、按体积分组、头尾分片哈希、全文件摘要、保留策略、CSV 清单生成。这一层不引用任何界面库,可以脱离窗口单独调用与测试dup_finder.py—— 图形界面层:窗口布局、后台扫描线程与主线程之间的消息队列、重复组表与文件明细表、勾选交互、导出与清理入口recycle.py—— 系统回收站接口封装:调用 Windows shell32 的 SHFileOperationW 把文件送进回收站,并逐条复查结果。提供「可撤销」这条底线demo_data.py—— 演示数据生成器:用固定种子造一批跨目录、跨文件名但内容相同的文件,供界面试用与自测共用同一份数据
运行环境:
运行环境:Windows 10/11(64 位),打包为单个 exe,不需要安装 Python 或任何运行库。整个程序运行期零第三方依赖,只用到 Python 标准库(界面用 tkinter,摘要用 hashlib,回收站用 ctypes 调系统接口),因此没有依赖升级、版本冲突这类负担。整个程序不联网,可以在断网环境下使用。
可扩展方向:
- 按相似度找「几乎重复」的图片:同一张照片的不同分辨率版本、加了水印的版本,现在不会被判为重复,可以引入感知哈希(aHash/dHash)作为第 2 步的替换项,三层框架不用动
- 硬链接合并:找到重复后不删除,而是用系统的硬链接接口把多份合并成一份,既省空间又不影响任何文件的内容与访问方式
- 自定义排除清单:现在只有全局的「跳过隐藏与系统目录」,可以加一个用户自己填的排除目录列表,用于跳过工程目录、素材库缓存等
- 扫描结果存档与续扫:大硬盘一次扫完耗时较长,可以把结果序列化保存,下次直接加载或接着扫,不用重头再来
- 定时自动扫描并出报告:配合系统计划任务,每周固定时间跑一遍并把清单邮件发出,适合长期维护素材盘的人
- 按扩展名/目录分组统计:除了「能释放多少空间」,再按类型给出「哪类文件重复最多」的报表,帮助用户找到重复的根源
本工具免费提供使用,但不对外分发源码。如果你需要在本工具基础上做功能调整、对接内部系统或长期维护,可以联系我们做二次开发,源码与工程文档可在定制合作中一并交付。
需要其它功能?可以定制
这个工具只做一件事:硬盘里同一张图存了七八份,占了几十 G,名字不一样、目录不一样,肉眼判断不出哪些是同一份,不敢乱删。如果你的场景还需要别的处理,可以联系我们做二次开发,按你的实际数据格式和流程定制。
常见的定制需求:
- 按你的素材管理习惯定制判定规则(例如把「只差 EXIF 的同一张照片」也算作重复)
- 改成服务器 / 共享盘场景的版本,扫描网络位置并按部门输出清理建议
- 把清理动作换成「移动到指定归档目录」而不是回收站,配合你们自己的备份流程
- 集成到素材入库流程里,新文件进来时自动检查是否已存在,从源头减少重复
- 联系方式:邮箱/微信见站内「联系我们」页面,或在本文下方留言
- 定制说明:请附上你的数据样例(脱敏即可)和期望结果,方便快速评估工作量
- 交付形式:定制项目可交付可执行程序,按需一并交付源码与工程文档,支持长期维护
使用说明与免责声明
- 本工具在本地运行,不联网、不上传任何数据到服务器。
- 涉及文件改写的功能,请在使用前备份原始文件。
- 软件完全免费,可自由使用与转发,请勿用于倒卖。
- 因误操作造成的数据问题请自行承担。

