go调用http接口必须先检查状态码(200–299)、再读取resp.body(用io.readall或io.copy)、最后defer resp.body.close(),否则易致空文件、panic或too many open files。

接口返回数据通常是 []byte 或需要序列化
Go 中调用 HTTP 接口(比如用 http.Get)拿到的响应体是 io.ReadCloser,不是直接可写入文件的字符串或结构体。常见错误是忽略响应体读取和关闭,导致文件为空或 panic。
- 必须用
io.ReadAll或bufio.NewReader读取全部内容,否则写入的是空数据 - 记得调用
resp.Body.Close(),否则连接不释放,可能触发too many open files - 如果接口返回 JSON,你可能想先
json.Unmarshal再格式化写入,但「写入文件」这一步本身只关心字节流——直接保存原始响应更简单、更保真
用 os.WriteFile 最简写法(适合小响应)
这是最直接的方式,适合响应体不大(比如几 MB 以内)、不需要额外处理的场景。它自动处理打开、写入、关闭三步,不易出错。
resp, err := http.Get("https://api.example.com/data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
err = os.WriteFile("output.json", body, 0644)
if err != nil {
log.Fatal(err)
}
注意:os.WriteFile 是覆盖写入;如果要追加,得换 os.OpenFile + Write。
大文件或流式写入要用 io.Copy 避免内存暴涨
当接口返回几十 MB 甚至更大的数据(比如导出 CSV、二进制文件),用 io.ReadAll 会把整个响应加载进内存,容易 OOM。此时应绕过内存缓冲,直接流式写入。
- 用
os.Create打开文件获取*os.File - 用
io.Copy将resp.Body直接拷贝到文件,底层使用 32KB 缓冲区,内存占用恒定 - 别忘了检查
io.Copy的返回值——它可能只写入部分数据就出错
f, err := os.Create("large-download.bin")
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
if err != nil {
log.Fatal(err)
}
写入前需判断状态码和 Content-Type
很多同学跳过这步,结果把 404 页面 HTML 或 500 错误响应当成正常数据写进文件,后续解析全崩。这不是写法问题,是逻辑缺失。
- 检查
resp.StatusCode是否在 200–299 范围,否则提前报错或跳过 - 检查
resp.Header.Get("Content-Type"),比如是否含application/json或text/csv,避免下载到重定向 HTML(text/html) - 某些 API 在失败时仍返回 200,但 body 里有
{"error": "..."}——这种需业务层解析判断,不能只靠 HTTP 状态码
最容易被忽略的是:没校验状态码就直接读 resp.Body,而有些服务对错误请求也返回非空 body,导致你存了一堆无用内容。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











