最可靠方法是用Directory.GetFiles递归获取全路径后计算SHA256哈希值比对,需过滤隐藏文件、避免硬链接重复计入、使用流式读取防内存溢出,并以相对路径为key存入Dictionary进行集合运算找差异。

用 Directory.GetFiles 配合哈希比对最可靠
直接比较文件名或大小容易漏判——同名但内容不同、重命名但内容相同都会出错。必须逐个计算文件哈希值才能准确识别逻辑差异。
推荐用 SHA256(兼顾速度与碰撞概率),避免 MD5(已不安全)或 SHA1(部分场景被弃用)。
注意:大文件别一次性 File.ReadAllBytes,要用流式哈希防止内存爆掉。
实操建议:
- 用
Directory.GetFiles(path, "*", SearchOption.AllDirectories)递归获取全路径,别用GetFiles(path, "*.*")——会漏掉无扩展名文件 - 路径统一转为
Path.GetFullPath再截取相对路径,否则C:\A\1.txt和C:/A/1.txt会被当成不同文件 - 对每个文件打开
FileStream,传给SHA256.ComputeHash(stream),别用File.OpenRead后不Dispose
用 HashSet 做差集比嵌套循环快得多
两个目录各生成一个 Dictionary(key=相对路径,value=哈希字节数组),再用集合运算找差异,比双重 for 循环快一个数量级,尤其文件数超千时明显。
常见错误现象:
— 把绝对路径当 key,导致同一文件在不同根目录下无法对齐
— 忘记忽略系统隐藏文件(如 Thumbs.db、.DS_Store),结果里全是噪音
实操建议:
- 构建 key 前先过滤:
if (File.GetAttributes(file) has FileAttributes.Hidden)跳过 - 用
new HashSet找 dir1 有但 dir2 没有的路径(dir1Keys).ExceptWith(dir2Keys) - 用
dir1Dict.Keys.Intersect(dir2Keys)找共有的路径,再比对哈希值是否一致
FileSystemWatcher 不适合做批量差异检测
有人想用 FileSystemWatcher 监听变化来“推断”差异,这完全走错方向——它只响应实时变更,无法回溯历史状态,也不能跨机器/备份目录比对,且事件可能丢失或重复触发。
使用场景明确:仅适用于“监控某目录后续增删改”这种流式场景,不是“此刻两个快照是否一致”的静态比对。
性能影响:
— 启动监听本身开销小,但误配 IncludeSubdirectories = true + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName 可能引发大量事件洪水
— 事件回调中做哈希计算会阻塞线程池,必须 await Task.Run(() => ComputeHash(...))
注意 NTFS 硬链接和符号链接的陷阱
如果目录里存在硬链接(mklink /H)或符号链接(mklink),Directory.GetFiles 默认会跟随并重复计入目标文件,导致“同一内容被算两次”,差异结果失真。
解决方案有限但有效:
- 用
File.GetAttributes(path)检查是否含FileAttributes.ReparsePoint,遇到就跳过(除非你明确要分析链接本身) - 调用 Win32 API
GetFileInformationByHandle获取dwFileAttributes和dwReserved0判断是否为链接——但需unsafe和P/Invoke,多数场景没必要 - 更务实的做法:比对前先用
cmd /c dir /AL扫一遍,人工确认是否存在链接干扰
真正难处理的是跨卷硬链接和挂载点,.NET 没提供轻量级检测方式,这时候只能接受“该路径下文件哈希一致即视为同步”,不深究底层指针关系。










