同步前先判断文件是否真的需要更新
直接复制所有文件既慢又容易覆盖新内容,关键得比对时间戳和哈希值。
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<string semaphoreslim></string>防止并发冲突。
另外,
FileSystemWatcher在网络共享路径上不可靠,Windows 默认禁用远程事件通知,此时只能退回到轮询
Directory.GetFiles+ 时间戳比对。
