io.Copy 不支持直接合并多个文件,需用循环+io.Copy 逐个追加,目标文件须以 os.O_WRONLY|os.O_CREATE|os.O_APPEND 模式打开;源文件逐个 os.Open 后拷贝,需及时关闭并检查每步错误。

Go 语言标准库的 io.Copy 本身不支持直接合并多个文件——它只做「单源到单目标」的流式拷贝。要合并多个文件,必须手动控制读写顺序,用循环 + io.Copy 逐个追加,且目标文件需以 os.O_APPEND 模式打开。
用 os.OpenFile 以追加模式打开目标文件
合并的本质是把多个源文件内容按序写入同一个目标文件末尾。如果用 os.Create 或 os.OpenFile 以 os.O_WRONLY | os.O_CREATE 打开,每次都会清空目标文件,导致只有最后一个文件被保留。
- 正确方式是用
os.O_WRONLY | os.O_CREATE | os.O_APPEND打开目标文件 - 首次写入时自动创建,后续写入始终追加到末尾
- 注意:
os.O_APPEND保证每次Write都发生在当前文件末尾,无需手动Seek
循环调用 io.Copy 合并多个 *os.File
io.Copy 是安全、高效、带缓冲的流拷贝函数,适合处理任意大小的文件(包括超大文件),不会爆内存。只要确保每个源文件都成功打开,并在拷贝后关闭即可。
- 源文件必须用
os.Open打开,不能用ioutil.ReadFile全部加载进内存 - 每个
io.Copy调用前检查错误,任一失败应立即返回(避免部分写入) - 记得用
defer src.Close()或显式Close(),防止文件句柄泄漏
func mergeFiles(dstPath string, srcPaths []string) error {
dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
return err
}
defer dst.Close()
for _, srcPath := range srcPaths {
src, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("failed to open %s: %w", srcPath, err)
}
if _, err := io.Copy(dst, src); err != nil {
src.Close()
return fmt.Errorf("failed to copy %s: %w", srcPath, err)
}
src.Close()
}
return nil
}
合并时注意文件头/分隔符与编码兼容性
纯字节追加虽简单,但实际中常需考虑语义完整性。例如合并日志文件时,可能需要在每个文件之间插入换行或时间戳;合并文本文件时,若源文件编码不一致(如 UTF-8 vs GBK),直接拼接会导致乱码。
立即学习“go语言免费学习笔记(深入)”;
- 二进制文件(如 PDF、图片)可直接追加,无编码问题
- 文本类合并建议统一为 UTF-8,并在每份内容前加注释行(如
// merged from file_a.log) - 若需插入分隔符,可用
dst.Write([]byte("\n---\n")),但要注意不能在第一次写入前加 - 不推荐用
bufio.Scanner逐行读取再拼接——会丢失空行、破坏二进制内容、且性能远低于io.Copy
最易忽略的是错误处理粒度:很多人只在最后检查 io.Copy 的返回值,却没对每个 os.Open 做校验,结果某个源路径不存在时程序 panic 或静默跳过。真正的健壮合并,必须让每个输入文件的可访问性、每个拷贝动作的完成度都明确可控。










