io.Copy 是高效可靠的文件拷贝方案,默认32KB缓冲;需检查源文件存在性、显式关闭目标文件;支持进度条(io.MultiWriter)、断点续传(Seek+CopyN)及Windows符号链接与权限处理。

用 io.Copy 实现基础文件拷贝,别自己循环读写
Go 标准库的 io.Copy 已经高效处理了缓冲、边界和错误传播,直接调用比手动 Read/Write 更可靠。它默认使用 32KB 缓冲区(io.DefaultBufSize),对大多数场景足够快。
- 源文件必须以
os.O_RDONLY打开,目标文件建议用os.O_CREATE | os.O_WRONLY | os.O_TRUNC - 记得在拷贝前检查源文件是否存在且可读:
os.Stat(src),避免io.Copy返回模糊的"invalid argument" - 拷贝完成后,显式调用
dst.Close()—— 否则可能丢失最后部分数据(尤其小文件或非阻塞写场景)
处理大文件时加进度条,用 io.MultiWriter 和自定义 Write
进度条不是靠轮询文件大小,而是拦截每次 Write 调用。把目标 *os.File 包一层,同时写入磁盘和计数器,再用 io.MultiWriter 组合多个写入目标(比如日志 + 进度回调)。
- 不要用
os.Getwd()拼接路径,改用filepath.Join处理跨平台路径分隔符 - 进度更新频率太高会拖慢拷贝速度,建议每 1MB 或每 100 次
Write触发一次回调 - 注意:如果目标路径已存在且是目录,
os.OpenFile会返回"is a directory"错误,需提前os.Stat判断类型
支持断点续传?得用 os.OpenFile 配合 Seek 和 io.CopyN
断点续传本质是跳过已写入部分,从指定偏移继续写。这要求目标文件以 os.O_APPEND 以外的方式打开(否则 Seek 无效),并手动控制读写位置。
- 先用
dst.Seek(0, io.SeekEnd)获取当前长度,作为起始偏移 - 源文件也需从该偏移开始读:
src.Seek(offset, io.SeekStart);注意检查src是否支持Seeker接口(如管道不支持) - 用
io.CopyN(dst, src, remaining)替代io.Copy,避免多写 - 续传前务必校验已写内容的哈希(如
sha256.Sum256前 N 字节),否则网络中断+重试可能导致文件错位
Windows 下复制失败常见于权限和符号链接,os.Readlink 和 syscall 得备着
Windows 对硬链接、符号链接、只读属性、ACL 权限更敏感。单纯 io.Copy 只能复制内容,无法保留元数据或处理链接目标。
立即学习“go语言免费学习笔记(深入)”;
- 判断是否为符号链接:
os.Readlink(path),若不报错说明是符号链接,应复制链接本身而非目标 - 设置只读属性需用
syscall.SetFileAttributes(Windows)或os.Chmod(Unix),不能只依赖os.CopyFile(Go 1.19+ 才有,且不跨平台保留属性) - 遇到
"Access is denied"错误,大概率是目标文件被其他进程占用(如编辑器锁住),需提示用户关闭相关程序
真正难的不是拷贝字节,而是判断“这个路径到底想表达什么”——是链接、是挂载点、是加密文件、还是 NTFS 压缩流。每种情况都得单独探测和分支处理。










