同步前需比对文件时间戳与哈希值,优先用LastWriteTimeUtc但须容忍FAT32的2秒精度误差;对时间差≤5秒且大小相同的文件跳过更新;FileSystemWatcher需去抖、队列化并加并发控制,网络共享应退化为轮询。

同步前先判断文件是否真的需要更新
直接复制所有文件既慢又容易覆盖新内容,关键得比对时间戳和哈希值。FileInfo.LastWriteTimeUtc 是基础判断依据,但要注意:NTFS 和 FAT32 时间精度不同(FAT32 只精确到 2 秒),同一台机器上也可能因时区或系统设置导致 LastWriteTime 和 LastWriteTimeUtc 不一致。更稳妥的做法是:对大小相同且修改时间相差 File.GetHash(比如 SHA256)校验内容——但别对大文件每次都算哈希,先用 Length 快速过滤。
单向同步:目标文件夹完全跟随源文件夹
本质是「增删改」三步操作,顺序不能错:先同步新增/变更文件,再删除目标中多余项。否则可能删掉还没来得及覆盖的旧版本。
- 遍历源目录所有
FileInfo,对每个文件检查目标路径是否存在;不存在就File.Copy,存在则按上一条逻辑判断是否需覆盖 - 遍历目标目录所有
FileInfo,若其相对路径在源目录中找不到对应项,调用File.Delete - 注意跳过子目录中的
bin、obj、.git等不需要同步的路径,可用Directory.EnumerateFiles(src, "*", SearchOption.AllDirectories)配合Path.GetRelativePath做白名单/黑名单过滤
双向同步:避免冲突的核心是「最后修改者胜出」
双向不是简单跑两遍单向逻辑——那样会把 A 改过的文件同步到 B 后,立刻又被 B 的旧版本覆盖回来。必须统一用一个「权威时间线」做决策:取两个位置中 LastWriteTimeUtc 更新的那个为准。但问题在于,如果网络延迟或时钟不同步,A 记录的「10:00」可能实际晚于 B 的「09:59」,这时就得引入版本向量或给每个文件加元数据文件记录上次同步时间点(如 file.txt.syncmeta)。
简易实现可接受一定风险:只同步「双方都存在且修改时间差 > 5 秒」的文件,并跳过「仅一方修改」的场景(即宁可不更新也不乱覆盖)。示例逻辑:
if (srcFile.Exists && dstFile.Exists)
{
if (Math.Abs((srcFile.LastWriteTimeUtc - dstFile.LastWriteTimeUtc).TotalSeconds) > 5)
{
var winner = srcFile.LastWriteTimeUtc > dstFile.LastWriteTimeUtc ? srcFile : dstFile;
File.Copy(winner.FullName, targetPath, true);
}
}用 FileSystemWatcher 做实时同步要格外小心
它适合监听变化,但不保证事件顺序或完整性——重命名+写入可能触发多个事件,Created 和 Changed 可能交错,甚至漏发。不要在 Changed 回调里直接调用 File.Copy,容易因文件正被写入而抛 IOException。正确做法是把变更路径暂存队列,用 Task.Delay(100) 去抖动,再批量处理;同时对同一路径加 ConcurrentDictionary 防止并发冲突。
另外,FileSystemWatcher 在网络共享路径上不可靠,Windows 默认禁用远程事件通知,此时只能退回到轮询 Directory.GetFiles + 时间戳比对。








