如何在Golang中进行文件复制操作_Golang文件复制与处理技巧

1次阅读

用 io.copy 复制文件最稳妥,它流式读写、内存友好且自动处理 EOF 和部分写;需手动创建目标文件、检查源文件类型、关闭文件句柄,并额外调用 os.Chmod/os.Chtimes 保留权限与时间戳。

如何在Golang中进行文件复制操作_Golang文件复制与处理技巧

io.Copy 复制文件最稳妥

go 标准库不提供类似 cp 的单函数封装,但 io.Copy 是最常用、最可靠的选择。它底层按缓冲区(默认 32KB)流式读写,内存友好,且自动处理 EOF 和部分写场景。

常见错误是直接用 os.ReadFile + os.WriteFile 处理大文件——这会把整个文件加载进内存,极易 OOM。

  • 务必先创建目标文件:用 os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, srcInfo.Mode())
  • 复制前建议用 os.Stat 检查源文件是否存在且为普通文件,避免复制目录或符号链接时出错
  • 记得关闭源和目标 *os.File,推荐用 defererrgroup 统一处理
src, _ := os.Open("a.txt") dst, _ := os.OpenFile("b.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) defer src.Close() defer dst.Close() io.Copy(dst, src) // 返回实际字节数和 Error

需要保留权限和时间戳?用 os.Chmodos.Chtimes

io.Copy 只负责内容,不会继承源文件的 mode、modtime、atime。若需完整克隆(如备份场景),必须手动设置。

注意:os.Chtimeswindows 上只支持修改修改时间(mtime),访问时间(atime)被忽略;linux/macOS 则两者都生效。

立即学习go语言免费学习笔记(深入)”;

  • 权限位要从源文件 os.FileInfo.Mode() 中提取,别硬写 0644 —— 可执行文件或 socket 文件会丢失可执行位或特殊属性
  • 调用 os.Chtimes 前确保目标文件已关闭,否则部分系统会返回 EBUSY
  • 如果目标路径父目录不存在,os.OpenFile 会失败,需提前用 os.MkdirAll(filepath.Dir(dstPath), 0755)

复制目录怎么办?别手写递归,用 filepath.WalkDir + os.MkdirAll

Go 1.16+ 的 filepath.WalkDir 比老版 filepath.Walk 更高效(避免重复 Stat),适合遍历源目录结构。

关键不是“怎么走”,而是“怎么建”:遇到目录就 os.MkdirAll,遇到文件就开 io.Copy。别用 os.Create 创建目录——它只建最后一级,且失败时不报明确错误。

  • 目标路径拼接要用 filepath.Join,别字符串拼接,否则在 windows 下路径分隔符出错
  • 跳过 .gitnode_modules 等目录?在 WalkDirFunc 中判断 dirEntry.IsDir() 和名称后 return nil 即可
  • 符号链接默认不跟随;如需解引用,用 os.Readlink + os.Symlink 单独处理,不要混进主逻辑

大文件或网络文件?考虑加进度与超时控制

io.Copy 没有进度反馈,用户无法感知卡在哪儿;http 响应体等 io.Reader 可能长时间无数据,需防 hang。

解决办法不是重写复制逻辑,而是包装 io.Readerio.Writer

  • 进度:用 io.MultiWriter 包裹目标 io.Writer,再加一个自定义 Write 方法统计字节
  • 超时:对网络文件,用 context.WithTimeout 控制 http.Client;对本地文件,可设 time.AfterFunc 监控阻塞,但更推荐用带 deadline 的 *os.FileSetReadDeadline 对普通文件无效,仅限网络连接)
  • 中断支持:把 io.Copy 放进 select + context.Done() 分支,配合 io.CopyN 分段复制,便于响应 cancel

真正难的不是“怎么复制”,而是“什么时候该停止”和“怎么让失败可追溯”——比如磁盘满时 io.Copy 返回 ENOSPC,但上层没检查 error 就继续,后续操作可能误删原文件。

text=ZqhQzanResources