filesystemwatcher 不可靠,仅适合轻量检查;真正可靠的增量依据需结合 lastwritetimeutc 与轻量哈希(如 xxhash32),并对大文件分段哈希;块级备份需自行实现或借助 memorymappedfile + span,usn 日志需管理员权限且限 ntfs。

用 FileSystemWatcher 捕获变化不靠谱
它只监听文件系统事件,但无法保证不丢事件(尤其批量写入、网络盘、杀毒软件干扰),更没法知道“哪个块变了”。实际生产中,FileSystemWatcher 仅适合触发轻量检查,不能直接当作增量依据。
- 事件可能被合并或丢失,比如连续重命名+写入,只收到一个
Changed - 文件被剪切粘贴到同一卷时,
Created和Deleted可能不触发 - 没提供校验信息,无法区分内容是否真变——比如只改了时间戳
真正可靠的增量依据是文件元数据 + 内容哈希
必须结合 LastWriteTimeUtc 和轻量哈希(如 XXHash32 或 SHA256 前 8 字节)来判断是否需备份。只比对时间戳会漏掉“改完又改回”的情况;只比哈希又太慢——所以折中:先比时间,再对“疑似变更”文件算小范围哈希。
- 跳过
ReadOnly或Hidden属性异常的文件,避免UnauthorizedAccessException - 对大于 1MB 的文件,建议只哈希前 64KB + 后 64KB(覆盖 header 和 footer 变化),而非全量
- 记录上次备份时间点用
DateTimeOffset,别用DateTime.Now(时区/夏令时风险)
FileStream 分块读取时容易卡在稀疏文件或权限错误
直接 new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read) 在遇到符号链接、加密文件或正在被其他进程独占写入时,会抛 IOException 或 UnauthorizedAccessException,导致整个备份中断。
- 加
FileOptions.SequentialScan提升大文件顺序读性能 - 用
try/catch包住单个文件处理,失败时记录path和exception.Message到日志,继续下一个 - 对路径做
Path.GetFullPath()标准化,避免..\绕过白名单校验 - 不要用
File.ReadAllBytes()—— 它会把整个文件拉进内存,1GB 文件直接 OOM
备份块(block-level)不是 .NET 原生支持的功能
.NET 没有内置“按修改块备份”的 API。所谓“块”,实际是你自己定义的逻辑单元:比如固定 64KB 分块,用 Span<byte></byte> 逐块计算差异哈希;或者用第三方库如 RdiffSharp 做二进制差分。但注意:RdiffSharp 不支持流式处理,必须加载基准文件全量到内存。
- 若真要块级,推荐用
MemoryMappedFile+Span<byte></byte>遍历,避免 GC 压力 - 块哈希结果存为
Dictionary<string byte></string>(key 是块偏移 + 长度,value 是哈希),别存成文件——IO 太重 - Windows 上可调用
BackupReadAPI 获取 USN 日志,但它需要管理员权限,且只适用于 NTFS 卷










