Go复制文件最稳妥用io.Copy,需显式指定os.O_TRUNC和0644权限;移动优先用os.Rename,跨设备时退化为复制+删除并同步元数据。
io.Copy 最稳妥Go 标准库没有直接的 os.CopyFile(直到 Go 1.21 才加入),但用 io.Copy + 打开两个文件是最通用、最可控的方式。它不依赖内存大小,适合大文件,且能捕获底层 I/O 错误。
常见错误是忽略 Close() 或没检查 os.OpenFile 的写入权限,导致复制后文件为空或 permission denied:
src, err := os.Open("source.txt")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.OpenFile("dest.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
log.Fatal(err)
}
os.O_TRUNC 很关键:不加会导致目标文件末尾追加而非覆盖0644 要显式指定,否则在某些系统上可能继承源文件的执行位(不安全)os.Cre
ate 替代 os.OpenFile——它固定为 O_CREATE|O_WRONLY|O_TRUNC,但无法控制权限位(旧版 Go 中权限默认是 0666 & ~umask,不可靠)os.Rename
移动(同磁盘)本质是重命名,os.Rename 是原子操作、零拷贝、最快也最安全。只要源和目标在同一个文件系统,它就成功;跨分区会报 invalid cross-device link 错误。
别试图用“复制 + 删除”模拟移动——这既不原子,又容易在中间失败导致数据丢失:
os.Rename,成功就完事syscall.EXDEV(Linux)或 ERROR_NOT_SAME_DEVICE(Windows),才退化为复制 + 删除dst.Close() 后调 dst.Sync(),再删)os.CopyFile
如果项目可锁定 Go 1.21+,os.CopyFile 是最简方案:它内部自动处理缓冲、权限、atime/mtime 保留(可选),并返回明确错误类型(如 os.ErrInvalid、os.ErrPermission)。
但它不处理跨设备移动,仅复制。想移动仍得自己组合逻辑:
err := os.CopyFile("src.bin", "dst.bin")
if err != nil {
log.Fatal(err)
}
os.Chmod(dst, 0644))os.Chtimes 同步 FileInfo.ModTime()
io.Copy + io.MultiWriter
真正生产环境的移动函数,得同时处理同设备 rename、跨设备 copy+remove、权限修复、时戳同步。容易被忽略的是 Windows 下对只读文件的处理:
os.Chmod(dst, 0644)
os.Rename 在 Windows 可能失败,需提前 os.Chmod(src, 0644)
fi, _ := src.Stat() 获取原始 ModTime 和 Mode(),再分别调 os.Chtimes 和 os.Chmod
os.Remove,失败则尝试 os.Chmod(src, 0644); os.Remove(src)(Windows 常见)这些细节不写进封装函数,迟早在线上遇到“文件移动一半消失”或“权限错乱导致后续进程拒绝访问”的问题。