filesystemwatcher 实时监听需谨慎:事件顺序不可靠,易漏报或重复;应缓存路径异步批量处理,结合快照对比(路径差集+时间戳/哈希校验)识别增删改与高概率重命名。

用 FileSystemWatcher 实时监听,但别直接信它的事件顺序
它确实能捕获创建、删除、重命名等动作,但实际中常漏报或重复触发——尤其在批量操作(如解压、同步工具写入)时,Changed 事件可能被合并,Rename 可能拆成 Created + Deleted。别把业务逻辑绑死在单个事件上。
- 务必启用
IncludeSubdirectories = true,否则子目录变更不会上报 - 设置
NotifyFilter时,NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName是最常用组合;加Attributes或Security会显著拖慢性能 - 事件回调里不要做耗时操作(如写数据库、发 HTTP 请求),用
ConcurrentQueue<string></string>缓存路径,另起线程批量处理 - Windows 上对 NTFS 卷更稳定,但若监控网络驱动器(如
Z:\),FileSystemWatcher很容易静默失效,得配合定期快照兜底
手动快照对比:用 GetFileSystemInfos + 哈希跳过内容比对
两次扫描间取差集,核心不是“比文件内容”,而是“快速识别增删改”。直接读全量文件内容算 MD5?太慢。改名和属性变更也得识别出来。
- 每次快照存一个
Dictionary<string length datetime lastwritetime string hash></string>,键是FullPath -
Hash不必是完整内容哈希:对小文件(Length )算 <code>MD5;大文件只取开头 4KB + 结尾 4KB 拼接后哈希,足够区分绝大多数修改 - 对比时先按路径找增删(字典 key 差集),再对共有的路径比
LastWriteTime和Hash—— 时间戳变但哈希没变?大概率只是访问时间更新,可忽略 - 注意:NTFS 的
LastWriteTime精度只有 100ns,但 FAT32 是 2s,跨卷对比时别依赖毫秒级时间判断
绕不开的重命名检测:靠 CreationTime 和 LastWriteTime 组合推断
文件系统不记录“原路径”,所以纯靠两次快照无法 100% 确认重命名。但可以高概率识别:同一文件在两次快照中长度一致、哈希一致,但路径不同,且新文件 CreationTime 接近旧文件 LastWriteTime(误差
- 别只看
CreationTime == LastWriteTime,有些备份工具会重置创建时间 - 如果旧路径文件已消失,新路径文件存在且满足上述条件,记为
RenamedFrom: oldPath - 遇到同名文件反复删建(如日志轮转),这种推断会误判,需加白名单路径过滤(如
*.log) - Windows 上可通过
GetFileInformationByHandle调用底层 API 获取文件 ID(FileIndexLow/High),但 .NET 标准库不暴露,得用System.IO.File.GetAttributes+ P/Invoke,权衡维护成本
性能临界点在哪?10 万文件以上必须分块 + 异步扫描
单次调用 Directory.GetFileSystemEntries 扫整个盘?内存暴涨、UI 冻结、还容易被杀毒软件拦截。10 万文件是明显分水岭。
- 用
Directory.EnumerateFileSystemEntries(返回IEnumerable<string></string>),边遍历边处理,不缓存全量路径 - 按目录层级分块:先扫根下一级所有子目录路径,再用
Parallel.ForEach分配到线程池,每块限 500 个子目录 - 对每个子目录,用
new DirectoryInfo(path).GetFileSystemInfos(),并设置SearchOption.TopDirectoryOnly避免递归嵌套开销 - 别在 UI 线程等结果,用
Task.Run包裹扫描逻辑,完成后通过Dispatcher.Invoke或SynchronizationContext回 UI 更新进度
真正难的不是代码怎么写,是当用户一边拷贝几十 GB 视频一边点“刷新”时,你怎么让程序既不卡死、又不把临时文件(~$xxx.docx、.part)当成新增文件上报。这些边界情况,得靠日志埋点+真实场景压测才能揪出来。










