本文介绍如何使用 io.copy 高效、简洁地将 io.readcloser(如 http 响应体)流式写入本地文件,兼顾内存占用低、cpu 开销小与代码可读性高三大目标。
本文介绍如何使用 io.copy 高效、简洁地将 io.readcloser(如 http 响应体)流式写入本地文件,兼顾内存占用低、cpu 开销小与代码可读性高三大目标。
在 Go 应用开发中,常需将网络响应(如 http.Response.Body,类型为 io.ReadCloser)直接保存为本地文件——例如下载大文件、代理转发或日志归档等场景。此时,避免一次性加载全部内容到内存是关键,而 io.Copy 正是为此类流式传输量身定制的标准库函数。
io.Copy(dst io.Writer, src io.Reader) 内部采用固定缓冲区(默认 32 KiB)循环读取并写入,无需手动管理 buffer、不分配额外切片,且自动处理 EOF 与错误传播。它既不会因缓冲区过大而浪费内存,也不会因过小而频繁系统调用拖慢性能——Go 团队已针对通用 I/O 场景做了充分权衡。
以下是最简、最健壮的实现示例:
outFile, err := os.Create("output.bin")
if err != nil {
log.Fatal("无法创建输出文件:", err)
}
defer outFile.Close()
// 流式复制:自动分块读写,零内存拷贝开销
_, err = io.Copy(outFile, res.Body) // res.Body 是 io.ReadCloser
if err != nil {
log.Fatal("写入文件失败:", err)
}⚠️ 关键注意事项:
- 必须显式调用 res.Body.Close()(或确保其被 defer 关闭),否则连接无法复用,可能引发资源泄漏;若 io.Copy 后还需读取响应头等信息,请勿提前关闭。
- os.Create 会截断已有文件;如需追加,请改用 os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)。
- 若目标文件路径涉及目录层级(如 "data/logs/app.log"),请预先调用 os.MkdirAll(filepath.Dir(filename), 0755) 创建父目录。
- 对于超大文件或高可靠性场景,建议在 io.Copy 后校验 outFile.Sync() 确保数据落盘(注意:会增加延迟,按需启用)。
总结:io.Copy 是 Go 标准库中流式 I/O 的黄金标准——它抽象了底层细节,提供了最佳的性能/可读性平衡。与其自行实现带缓冲的循环读写,不如信任经过生产验证的 io.Copy,让代码更短、更稳、更快。










