os.stat 不能判断文件内容是否一致,因其仅返回修改时间、大小等元数据,而大小相同或时间相同均不保证内容一致;md5 是轻量且适合本地同步的校验方式,但不防碰撞,仅作确定性比对。

为什么 os.Stat 不能直接判断文件内容是否一致
因为 os.Stat 只返回元数据(修改时间、大小等),而两个文件可能大小相同、时间不同,或时间相同、内容被覆盖过——这些都会导致误判。MD5 是最轻量且足够用于本地同步的校验方式,但注意:它不防碰撞,仅作确定性比对用。
- 别用
time.ModTime做唯一依据,NFS、某些编辑器保存逻辑会导致时间戳不准 - 小文件(md5.Sum;大文件务必用
io.Copy+hash.Hash流式计算,否则 OOM - Windows 下注意路径分隔符统一用
filepath.Join,避免硬写"\"或"/"
如何安全地计算大文件的 MD5 而不爆内存
核心是用 hash/md5 的流式接口,配合 os.Open 和 io.Copy,边读边哈希,全程只占几 KB 内存。
- 必须用
defer f.Close(),漏关文件句柄在批量同步时会快速触发too many open files - 别用
ioutil.ReadFile—— 它已弃用,且对大文件直接 panic - 示例关键片段:
f, _ := os.Open(path) defer f.Close() h := md5.New() io.Copy(h, f) sum := h.Sum(nil)
同步逻辑里最容易漏掉的三个状态分支
文件同步不是“源有目标无就复制”,而是要穷举 src/target 的存在性与哈希匹配关系。漏掉任一组合,就会丢文件或反复覆盖。
-
src 存在 && target 不存在→ 复制 -
src 存在 && target 存在 && md5 不同→ 覆盖(注意先写临时文件再os.Rename) -
src 不存在 && target 存在→ 删除(加开关控制,默认不删,避免误操作) - 别忽略
os.IsNotExist(err)判断,直接if fi != nil会掩盖权限错误等真实问题
为什么 filepath.Walk 比递归 ReadDir 更适合同步扫描
filepath.Walk 自动处理符号链接、权限拒绝、循环目录,而手动递归容易卡死或 panic。但它默认不保证顺序,也不跳过隐藏文件——这些得自己过滤。
立即学习“go语言免费学习笔记(深入)”;
- 用
strings.HasPrefix(fi.Name(), ".")过滤隐藏文件(如.git、.DS_Store) - 遇到
os.ErrPermission时,Walk会继续,但你的回调函数必须检查err != nil并跳过,否则可能 panic - 不要在
WalkFunc里做耗时操作(如逐个算 MD5),先收集路径,再并发处理——否则 I/O 等待拖慢整体速度
os.Open 错误会让整个 walk 中断,而拼错一个 filepath.Join(dstRoot, relPath) 可能静默写到错误位置。这些地方没有银弹,只能每处都加 if err != nil 分支并打日志。










