go 复制文件最稳妥方式是 os.open + os.create + io.copy,需显式 close(先 dst 后 src)、调用 dst.sync()、补全权限与时间戳、确保父目录存在,大文件勿盲目并发,校验仅在必要时添加。

Go 里复制文件,最稳妥的方式就是 os.Open + os.Create + io.Copy,不是“可以”,而是“应该”这么干。其他花式写法(比如手动 Read/Write 循环、用 ioutil)要么过时,要么漏关键细节,生产环境容易翻车。
为什么必须显式 Close() 且顺序不能错
很多人只记得 defer src.Close() 和 defer dst.Close(),但没注意:如果目标文件已存在且是只读的(尤其 Windows),os.Create 会直接失败;而如果复制中途出错,dst 可能根本没成功打开,这时 defer dst.Close() 会 panic。
- 正确做法:先判空再 defer,或确保
dst打开成功后再加defer - 关闭顺序很重要:必须先
dst.Close(),再src.Close()—— 否则在 Windows 下,你可能删不掉或重命名不了源文件(句柄还被 dst 持有) - 复制完立刻调用
dst.Sync(),否则断电或 crash 时缓存里的数据可能永远不落盘(对配置文件、数据库快照等关键数据是硬要求)
io.Copy 不动元数据,权限和时间戳得自己补
io.Copy 只管字节流,它完全不知道文件有没有执行权限、是不是昨天改的。如果你 cp 一个脚本过去发现不能 ./xxx,八成是忘了设 mode。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
src.Stat()拿到os.FileInfo,再调os.Chmod(dstPath, fi.Mode()) - 时间戳用
os.Chtimes(dstPath, fi.ModTime(), fi.ModTime())(两个时间设一样就行) - Windows 下若目标已存在且只读,得先
os.Chmod(dstPath, 0644)再覆盖,否则os.Create直接报 permission denied -
os.Chtimes在 FAT32 或某些网络文件系统上可能静默失效,别依赖它 100% 成功
大文件、多文件、校验——别一上来就并发 or 加 hash
看到“大文件”就想开 goroutine 或加 MD5?先停一下。磁盘 I/O 是瓶颈,不是 CPU。
- 普通 SATA 硬盘,并发 2–4 个
io.Copy就到吞吐峰值;盲目开 20 个 goroutine,反而让磁头疯狂寻道,总耗时更长 - 校验不是必须的:如果只是本地拷贝,
io.Copy出错会立刻返回,失败即失败;只有跨网络、落盘不可信、或业务强要求完整性时,才值得加crypto/md5(用io.TeeReader边读边算,避免二次读取) - 断点续传纯 Go 实现成本很高,需要管理 offset、临时文件、原子替换;真有这需求,优先走
rsync或 HTTP Range
真正容易被忽略的,是 dst.Sync() 和父目录检查——os.Create 不会自动建多级目录,os.MkdirAll(filepath.Dir(dst), 0755) 这一行,线上部署时漏掉一次,就卡住整个流程。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










