io.copy 复制文件不生效主因是忽略元信息处理与路径检查:需确保源文件可读、目标父目录存在(用 os.mkdirall)、复制后调用 dst.close(),并检查 io.copy 返回的 err;保留权限和时间戳须手动调用 chmod/chtimes。

Go 复制文件时 io.Copy 为什么有时不生效?
因为 io.Copy 只负责数据搬运,不处理文件元信息(如权限、时间戳),也不保证目标路径存在。常见现象是:没报错,但目标文件为空或复制失败——大概率是目标目录不存在,或源文件打开失败却没检查 err。
- 务必先用
os.Stat或os.Open检查源文件是否存在且可读 - 目标路径的父目录必须提前创建,
os.Create不会自动建目录,要用os.MkdirAll(filepath.Dir(dst), 0755) - 复制后记得
dst.Close(),否则文件可能写入不全(尤其小文件,缓冲未刷出) - 不要直接用
io.Copy(dst, src)后就认为完事,要检查返回的n, err,err非空才真正出问题
需要保留权限和 ModTime?别只用 io.Copy
io.Copy 只复制内容,os.FileInfo 中的 Mode() 和 ModTime() 得手动设置。否则 Linux 下执行权限、Windows 下只读属性都会丢失,cp -p 的效果在 Go 里得自己补。
- 复制完成后,用
dst.Chmod(srcInfo.Mode())设置权限(注意:Windows 对部分 mode 位忽略) - 用
dst.Chtimes(srcInfo.ModTime(), srcInfo.ModTime())同步时间戳 - 如果源文件是符号链接,
os.Open会自动解引用;要保留链接本身,得用os.Readlink+os.Symlink单独处理 - 避免对设备文件、管道等特殊文件调
Chmod,可能触发EPERM,建议用srcInfo.Mode() & os.ModeType == 0过滤普通文件
os.CopyFile(Go 1.21+)能替代手写逻辑吗?
能,但只在满足最小交集场景时够用:纯内容复制、不关心 symlink/权限/时间戳、目标路径已确保可写。它内部就是封装了 io.Copy + 基础错误检查,没有额外 magic。
- Go 1.21 才引入,旧版本项目无法使用,升级前确认兼容性
- 它不会创建父目录,
os.CopyFile("a/b/c.txt", "x/y/z.txt")中x/y不存在会直接返回no such file or directory - 不支持自定义 buffer 大小,内部固定用 32KB,大文件吞吐不如手动配
io.CopyBuffer - 错误信息较笼统(比如统一返回
copy file: ...),调试时不如自己控制 open/close 步骤来得清晰
大文件复制卡住或内存暴涨?检查你的 buffer 和 context
默认 io.Copy 内部用 32KB buffer,对 GB 级文件没问题;但若你误用了 bytes.Buffer 或一次性 ioutil.ReadAll,就会 OOM。另外,没设超时的复制在 NFS 或挂起的 USB 设备上可能无限阻塞。
- 绝对不要用
io.Copy(ioutil.Discard, src)来“跳过”大文件——它仍会读完整个内容,只是丢弃,CPU 和 I/O 一样不省 - 需要进度反馈或中断能力,用
io.CopyN分块 +select检查ctx.Done() - 若目标是网络存储(如 S3),别用本地文件复制模式,应改用对应 SDK 的流式上传接口
- Linux 上复制到 ext4 加密卷时,某些内核版本对 >2GB 文件有 writev 性能退化,此时显式用
io.CopyBuffer配 1MB buffer 可明显提速
文件复制看着简单,但权限、路径、上下文、底层 FS 行为这些细节一叠加,很容易在某个环境突然失效。最稳妥的做法不是找“最短代码”,而是明确你到底要什么:仅内容?带属性?可中断?跨文件系统?——每个选项都对应不同的检查点和 fallback 路径。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











