C#实现云同步文件夹逻辑 C#如何设计一个类似Dropbox的本地文件同步代理

2次阅读

同步代理的核心职责是持续监控本地目录变化、比对云端状态、按需上传下载,并处理冲突与断点续传;关键在于区分变更事件与最终状态,基于文件元数据(大小+etag/md5)判断同步时机,维护本地状态缓存,网络操作须带重试与取消令牌。

C#实现云同步文件夹逻辑 C#如何设计一个类似Dropbox的本地文件同步代理

同步代理的核心职责是什么

一个本地文件同步代理不是简单地复制粘贴文件,它要持续观察本地目录变化、比对云端状态、按需上传下载,并处理冲突和断点续传。关键在于:它必须区分「变更事件」和「最终状态」——FileSystemWatcher 触发的 Changed 事件非常频繁且可能重复,不能直接拿它当同步指令;真正该同步的是文件内容哈希或最后修改时间戳确认过的终态。

  • 同步粒度应基于文件元数据(大小 + ETagMD5)而非仅靠时间戳,windows 文件系统时间精度低,NFS 或虚拟机场景下容易误判
  • 必须维护本地状态缓存(如 sqlite 数据库存储每个文件的 local_hashremote_versionis_deleted),否则每次启动都要全量扫描比对
  • 所有网络操作必须带重试(指数退避)和取消令牌(CancellationToken),避免卡死整个代理进程

如何用 FileSystemWatcher 安全捕获真实变更

FileSystemWatcher 是入口,但默认配置极易漏事件或触发多次。它本身不保证事件顺序,也不合并连续写入(例如 word 保存会触发 3–4 次 Changed)。

  • 设置 NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,禁用 FileNameDirectoryName,减少重命名/移动带来的干扰
  • 必须启用 IncludeSubdirectories = true,但为避免递归监听导致句柄耗尽,建议只监听用户指定的根路径,子目录由程序逻辑遍历发现
  • 对每个 Changed 事件,立刻启动一个带延时的去抖任务(例如 Task.Delay(1000, token)),等静默期结束后再读取文件大小和哈希——这是避免“写一半就同步”的唯一可靠方式
  • 删除事件(Deleted)需单独处理并记录到本地状态库,不能依赖后续扫描推断

上传/下载如何避免阻塞和重复

同步代理常因大文件上传卡住 ui 或拖慢扫描。所有 I/O 必须异步且可中断,同时防止同一文件被多次排队。

  • 使用 HttpClient.PutAsync() 或分块 POST 上传,配合 IProgress<long></long> 更新进度,上传前先校验本地哈希是否已存在于云端(查 /api/v1/file/info?hash=xxx
  • 下载使用 Range 请求支持断点续传,把临时文件写到 .sync_tmp_abc123,完整校验哈希后再原子重命名为目标名
  • 维护一个内存中 ConcurrentDictionary<String task></string>(key 为文件相对路径),提交新任务前先 TryRemove 旧任务并 await 其完成,防止并发上传同一文件

冲突检测与用户干预点在哪

自动覆盖不是好策略。Dropbox 式行为是:当本地和云端都修改过同一文件,保留两者,生成类似 report.docx (Your Conflicted copy 2024-05-22).docx 的副本。

  • 冲突判定依据是:本地有修改(local_hash != remote_hash)且云端也有更新(remote_version > local_version
  • 不要尝试自动合并二进制文件,文本文件也仅在明确标记为 text/plain 且启用 diff 工具时才走合并逻辑
  • 把冲突文件路径写入一个 conflicts.json 清单,供 GUI 层弹窗提示,或 CLI 输出警告行 —— 这个清单本身也要同步到云端,让多端知道“此处有未决冲突”

本地状态库结构稍复杂,但少它一天都跑不稳;哈希计算别图省事用 File.ReadAllBytes(),大文件必须流式计算;还有,别忘了 Windows 上长路径(>260 字符)需要在 app.manifest 里启用 longPathAware

text=ZqhQzanResources