io.Copy 是 Go 中复制文件最常用的方式,因其内置32KB缓冲、适配任意io.Reader/io.Writer、性能优于手动循环,但不处理元信息、断点续传或进度反馈。

为什么 io.Copy 是 Go 中复制文件最常用的方式
因为 io.Copy 内部做了缓冲处理,自动使用 32KB 的默认缓冲区,避免逐字节读写,性能远高于手动 Read/Write 循环。它只关心 io.Reader 和 io.Writer 接口,天然适配文件、网络流、内存 buffer 等任意组合。
但要注意:它不复制文件元信息(如权限、修改时间、owner),也不支持断点续传或进度反馈 —— 这些得自己封装。
用 os.Open 和 os.Create 配合 io.Copy 完成基础复制
这是最常见也最稳妥的组合。必须显式关闭源和目标文件句柄,否则可能造成资源泄漏或写入不完整。
-
os.Open打开源文件,只读模式;os.Create创建目标文件,覆盖写入(若存在) - 复制完成后,**必须先
dst.Close(),再src.Close()** —— 否则在 Windows 下可能因句柄占用导致删除/重命名失败 - 如果目标路径的父目录不存在,
os.Create会报no such file or directory,需提前用os.MkdirAll
src, err := os.Open("source.txt")
if err != nil {
log.Fatal(err)
}
defer src.Close()
dst, err := os.Create("dest.txt")
if err != nil {
log.Fatal(err)
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
log.Fatal(err)
}
复制时保留文件权限和 ModTime 的补充操作
io.Copy 本身不处理文件属性,但 Go 标准库提供了 os.Stat 和 os.Chmod/os.Chtimes 来补全。
立即学习“go语言免费学习笔记(深入)”;
- 用
os.Stat获取源文件的os.FileInfo,从中提取Mode()和ModTime() -
os.Chmod(dst.Name(), fi.Mode())设置权限(注意:Linux/macOS 有效,Windows 仅部分生效) -
os.Chtimes(dst.Name(), fi.ModTime(), fi.ModTime())同步修改时间(访问时间可设为相同或不同) - 若源文件是符号链接,
os.Stat返回目标文件信息;需用os.Lstat判断并决定是否复制链接本身
大文件复制时如何避免阻塞主线程或超时
直接调用 io.Copy 是同步阻塞的,单个大文件可能卡住 goroutine。实际服务中应结合上下文控制:
- 用
context.WithTimeout包裹复制过程,防止无限等待(例如网络文件系统挂起) - 对超大文件(>1GB),可改用
io.CopyBuffer自定义缓冲区大小(如64 * 1024),平衡内存占用与吞吐 - 若需实时进度,不能依赖
io.Copy返回值(它只返回总字节数),而应包装一个带回调的io.Reader,在每次Read后触发更新 - 多个文件并发复制时,注意限制 goroutine 数量(例如用
semaphore或worker pool),避免打开过多文件句柄触发too many open files
权限、时间、上下文、缓冲区、并发控制 —— 这些都不是 io.Copy 自己做的事,但生产环境几乎每一条都绕不开。










