一个 Windows 桌面工具,核心差异点是文件夹级对比:找到重复文件后,可整文件夹地决定保留一侧、删除另一侧,而不是逐个文件挑挑拣拣。
版本:v0.15 · .NET Framework 4.8 + WPF · Win10/11 开箱即用,无需安装运行时。
- Windows 10 / 11(自带 .NET Framework 4.8)
方式一:直接运行(无需构建)
双击 publish/DuplicateFinder.exe方式二:从源码构建
cd src
dotnet build -c Release产物在 src/bin/Release/net48/DuplicateFinder.exe。
方式三:发布(生成便携目录)
cd src
dotnet publish -c Release -r win-x64 -o ../publish或直接在项目根目录运行:
.\build.ps1 -Publish- 模式选「单文件夹(复杂查重)」,选择根目录 A,点「开始扫描」。
- 左侧目录树只展示含重复文件的目录,括号内为自底向上累加的重复文件数。默认全部折叠,按需展开。
- 点击某个重复文件 → 右侧列出它的所有副本及其所在目录,以及每个目录与当前选中目录的文件夹相似度(Jaccard / IoU 计算,≥80% 高亮为「疑似整文件夹副本」)。
- 右侧文件支持 Ctrl/Shift 多选,底部「删除选中 (N)」批量删除。
- 工具栏支持忽略 0 字节文件(默认开启)、勾选「永久删除」跳过回收站(需二次确认)。
- 启动即双选:选择根目录 A + 根目录 B → 扫描后自动打开「目录对比」窗口,左右两栏各自展示重复文件,中间列出匹配副本对。
- 模式 A 中触发:右键某文件选「对比所在文件夹」→ 对比该文件所在目录与副本所在目录(复用已算哈希,不重算)。
- 点击文本(非小三角)也可展开/折叠目录。
- 级联展开:若展开后目录无直接重复文件且仅含一个子目录,自动级联展开该子目录。
- 右键菜单:
- 文件节点:打开文件 / 打开所在文件夹 / 删除。
- 目录节点:打开文件夹 / 删除(四选项——依旧删除目录 / 仅删当前目录重复文件 / 删当前目录及子目录重复文件 / 取消)。若删除会导致某重复组全部副本消失,弹窗警告。
- 文件列表右键:打开文件 / 打开所在文件夹 / 对比所在文件夹 / 删除。
- 相似度列表:双击打开对应文件夹;右键可打开 / 对比 / 删除文件夹。删除文件夹时根据是否含子目录、相似度是否 100% 智能弹出不同确认对话框。
- 键盘快捷键:选中文件后按
Del直接移入回收站(永久删除模式下仍需确认)。
- 默认删除进回收站;勾选「永久删除」则直接删除(所有删除入口均追加二次确认)。
- 删除后实时更新数据库;若某重复组被清理到只剩 1 个文件,该文件不再作为重复展示,对应目录计数与树节点自动收敛。
- 目录树增量裁剪而非整树重建——删除后展开状态保留,大数据量不卡顿。
- 扫描结果保存到程序目录
dupfinder.db(SQLite)。 - 启动时若存在数据库,弹窗询问是否加载上次结果。
- 关闭时若存在数据库,弹窗询问是否保留(下次启动可恢复)。
- 「清空缓存」按钮删除数据库并重置。
- 「并行度」默认 4,可自行修改(机械盘建议调低至 2)。
- 扫描自动跳过符号链接 / junction / reparse point。
- 无权限目录 / 被锁定文件记录日志后跳过,不中断扫描。
- 工具栏右上角二维码,点击放大后可跳转网站。
| 方面 | 实现 |
|---|---|
| 磁盘读取优化 | 先按文件大小分组,唯一大小瞬间判定孤立;≤1MB 全量 XxHash3;>1MB 仅读前 512K + 后 512K(最多 1MB) |
| 哈希算法 | System.IO.Hashing.XxHash3(seed=0),.NET 内置 |
| 相似度算法 | Jaccard / IoU:重复文件数 / (|A|+|D|− 重复文件数) × 100 |
| 持久化 | Microsoft.Data.Sqlite,支持断点续扫与对比复用 |
| 架构 | MVVM(CommunityToolkit.Mvvm)+ WPF + .NET Framework 4.8 |
| 删除 | SHFileOperation 进回收站;支持永久删除 + 二次确认 |
DuplicateFinder/
├── src/ # 源码
│ ├── DuplicateFinder.csproj # net48 + WPF 项目文件
│ ├── App.xaml(.cs) # 应用入口 + 全局异常处理
│ ├── MainWindow.xaml(.cs) # 主窗口
│ ├── Models/ # 数据模型(FileEntry, DupGroup, ScanSession, DirStat)
│ ├── Services/ # 核心服务
│ │ ├── ScanEngine.cs # 枚举 + 并行哈希调度
│ │ ├── HashCalculator.cs # XxHash3 分块策略
│ │ ├── ScanRepository.cs # SQLite + 内存缓存
│ │ ├── TreeBuilder.cs # 目录树构建 + 累计统计
│ │ ├── TreeReconciler.cs # 目录树增量裁剪
│ │ ├── ComparisonService.cs # 单文件对比 + 文件夹相似度
│ │ ├── FileDeleteService.cs # 删除服务(回收站/永久)
│ │ └── RecycleBin.cs # SHFileOperation P/Invoke
│ ├── ViewModels/ # MVVM ViewModel
│ │ ├── MainViewModel.cs # 主逻辑
│ │ ├── TreeItem.cs # 树节点
│ │ └── CompareItem.cs # 对比面板条目
│ ├── Views/ # 视图
│ │ └── DualCompareWindow.xaml(.cs) # 双目录对比窗口
│ └── Infrastructure/ # 基础设施
│ ├── ViewModelBase.cs # MVVM 基类
│ ├── RelayCommand.cs # ICommand 实现
│ ├── ScanProgress.cs # 扫描进度
│ ├── Logger.cs # 日志
│ └── CrashReporter.cs # 全局异常兜底
├── test/ # 核心算法端到端测试(41 项)
├── docs/ # 文档
├── build.ps1 / build.sh # 构建脚本
├── VERSION # 版本号
├── CHANGELOG.md # 版本历史
└── publish/ # 发布产物
- 大文件哈希仅在内容前缀 + 中段(共 1MB)层面判定重复,存在极小碰撞概率(设计上可接受)。
- 本项目为 WPF 桌面应用,仅支持 Windows;Linux / macOS 无法运行。
A Windows desktop tool whose core differentiator is folder-level comparison: after finding duplicate files, you can decide to keep one folder's copy and delete another folder's copy as a whole, rather than picking through files one by one.
Version: v0.15 · .NET Framework 4.8 + WPF · Works out of the box on Win10/11 with zero runtime installation.
- Windows 10 / 11 (comes with .NET Framework 4.8 pre-installed)
Option 1: Run directly (no build needed)
Double-click publish/DuplicateFinder.exeOption 2: Build from source
cd src
dotnet build -c ReleaseOutput: src/bin/Release/net48/DuplicateFinder.exe.
Option 3: Publish (portable directory)
cd src
dotnet publish -c Release -r win-x64 -o ../publishOr from the project root:
.\build.ps1 -Publish- Select "Single folder", choose root directory A, click "Start Scan".
- The directory tree on the left shows only directories containing duplicates, with cumulative duplicate file counts in parentheses (bottom-up accumulation). All collapsed by default.
- Click a duplicate file → the right panel shows all its copies and their directories, plus folder similarity for each directory relative to the selected file's directory (Jaccard / IoU formula; ≥80% highlighted as "likely whole-folder copy").
- Right panel files support Ctrl/Shift multi-select; the bottom "Delete Selected (N)" button batch-deletes.
- Toolbar options: ignore zero-byte files (on by default), "Permanent delete" checkbox (skips Recycle Bin with double confirmation).
- Startup dual-select: Choose root A + root B → after scanning, a "Directory Comparison" window opens automatically with left/right trees and a middle match list.
- Triggered from Mode A: Right-click a file and select "Compare Folder" → compares that file's directory with the copy's directory (reuses computed hashes, no re-scan).
- Click text (not just the arrow) to expand/collapse directories.
- Cascade expand: If an expanded directory has no direct duplicate files and contains exactly one subdirectory, that subdirectory auto-expands recursively.
- Right-click menu:
- File node: Open file / Open folder / Delete.
- Directory node: Open folder / Delete (4 options — Delete entire directory / Delete duplicates in current directory only / Delete duplicates in current directory and subdirectories / Cancel). Warns if deletion would eliminate all copies of any duplicate group.
- File list right-click: Open file / Open containing folder / Compare folder / Delete.
- Similarity list: Double-click to open the folder; right-click for Open / Compare / Delete folder. Smart confirmation dialogs based on whether the folder has subdirectories and whether similarity is 100%.
- Keyboard: Press
Delon a selected file to send to Recycle Bin (confirmation still required in permanent-delete mode).
- Default: send to Recycle Bin; check "Permanent delete" for direct deletion (all deletion paths add a second confirmation).
- Database is updated in real-time after deletion; if a duplicate group drops to 1 remaining file, that file is no longer shown as a duplicate — directory counts and tree nodes converge automatically.
- The directory tree uses incremental pruning instead of full rebuild — expanded state is preserved after deletion, and performance stays smooth even with large datasets.
- Results are saved to
dupfinder.db(SQLite) in the program directory. - On startup, if a database exists, a dialog asks whether to load the last scan.
- On close, if a database exists, a dialog asks whether to keep it (for next-time recovery).
- The "Clear Cache" button deletes the database and resets.
- Concurrency defaults to 4; lower it (e.g., 2) for mechanical hard drives.
- Symbolic links / junctions / reparse points are automatically skipped.
- Permission-denied directories and locked files are logged and skipped without interrupting the scan.
- A QR code in the top-right toolbar can be clicked to enlarge, then clicked again to open a website.
| Aspect | Implementation |
|---|---|
| Disk I/O optimization | Group by file size first; unique sizes are instantly isolated. ≤1MB: full XxHash3. >1MB: first 512KB + last 512KB only (1MB max read) |
| Hash algorithm | System.IO.Hashing.XxHash3 (seed=0), built into .NET |
| Similarity formula | Jaccard / IoU: matching / (|A| + |D| − matching) × 100 |
| Persistence | Microsoft.Data.Sqlite; supports resume-scan and comparison reuse |
| Architecture | MVVM + WPF + .NET Framework 4.8 |
| Deletion | SHFileOperation for Recycle Bin; permanent delete with double confirmation |
DuplicateFinder/
├── src/ # Source code
│ ├── DuplicateFinder.csproj # net48 + WPF project
│ ├── App.xaml(.cs) # Entry point + global crash handler
│ ├── MainWindow.xaml(.cs) # Main window
│ ├── Models/ # Data models (FileEntry, DupGroup, ScanSession, DirStat)
│ ├── Services/ # Core services
│ │ ├── ScanEngine.cs # Enumeration + parallel hash scheduling
│ │ ├── HashCalculator.cs # XxHash3 chunking strategy
│ │ ├── ScanRepository.cs # SQLite + in-memory cache
│ │ ├── TreeBuilder.cs # Directory tree + cumulative stats
│ │ ├── TreeReconciler.cs # Incremental tree pruning
│ │ ├── ComparisonService.cs # Single-file comparison + folder similarity
│ │ ├── FileDeleteService.cs # Deletion service (Recycle Bin / permanent)
│ │ └── RecycleBin.cs # SHFileOperation P/Invoke
│ ├── ViewModels/ # MVVM ViewModels
│ │ ├── MainViewModel.cs # Main logic
│ │ ├── TreeItem.cs # Tree node
│ │ └── CompareItem.cs # Comparison panel item
│ ├── Views/ # Views
│ │ └── DualCompareWindow.xaml(.cs) # Dual-directory comparison window
│ └── Infrastructure/ # Infrastructure
│ ├── ViewModelBase.cs # MVVM base class
│ ├── RelayCommand.cs # ICommand implementation
│ ├── ScanProgress.cs # Scan progress model
│ ├── Logger.cs # Logger
│ └── CrashReporter.cs # Global crash reporter
├── test/ # Core algorithm E2E tests (41 assertions)
├── docs/ # Documentation
├── build.ps1 / build.sh # Build scripts
├── VERSION # Version file
├── CHANGELOG.md # Version history
└── publish/ # Published output
- Large-file duplicate detection is based on content prefix + mid-section (1MB total read), with a negligible collision probability (acceptable by design).
- This is a WPF desktop application, Windows only; does not run on Linux / macOS.