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),直接拼接会导致乱码。
// merged from file_a.log)dst.Write([]byte("\n---\n")),但要注意不能在第一次写入前加bufio.Scanner 逐行读取再拼接——会丢失空行、破坏二进制内容、且性能远低于 io.Copy
最易忽略的是错误处理粒度:很多人只在最后检查 io.Copy 的返回值,却没对每个 os.Open 做校验,结果某个源路径不存在时程序 panic 或静默跳过。真正的健壮合并,必须让每个输入文件的可访问性、每个拷贝动作的完成度都明确可控。