必须检查响应状态码,否则可能将404、500等错误响应误作文件内容写入磁盘,导致损坏或空文件;应先判断resp.StatusCode >= 200 && resp.StatusCode < 300。

用 http.Get 发起下载请求时,必须检查响应状态码
直接调用 http.Get 后若不检查 resp.StatusCode,可能把 404、500 等错误响应当作文件内容写入磁盘,导致下载到损坏或空文件。
- 始终先判断
resp.StatusCode >= 200 && resp.StatusCode - 非成功状态应读取
resp.Body并关闭(避免连接复用异常) - 注意重定向:默认
http.DefaultClient会自动跟随,如需禁用,需自定义Client并设CheckRedirect
用 io.Copy 流式写入文件比一次性读取更安全
大文件下载时,io.Copy 按固定缓冲区(默认 32KB)分块传输,内存占用恒定;而 io.ReadAll 会把整个响应体加载进内存,易触发 OOM。
- 目标文件需以
os.O_CREATE | os.O_WRONLY | os.O_TRUNC模式打开 - 写入前建议用
defer f.Close()确保资源释放 - 若需进度提示,可包装
resp.Body或目标*os.File为带计数的 reader/writer(如io.TeeReader)
完整可运行示例:带错误处理和流式保存
package main
<p>import (
"io"
"net/http"
"os"
)</p><p>func main() {
resp, err := http.Get("<a href="https://www.php.cn/link/de829cc41d27f07c17771b5027167353">https://www.php.cn/link/de829cc41d27f07c17771b5027167353</a>")
if err != nil {
panic(err)
}
defer resp.Body.Close()</p><pre class='brush:php;toolbar:false;'>if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic("HTTP error: " + resp.Status)
}
out, err := os.OpenFile("downloaded.zip", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
panic(err)
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
panic(err)
}}
常见陷阱:忽略 Content-Length 和超时控制
服务端未设置 Content-Length 时,io.Copy 仍能工作(靠 EOF 判断结束),但无法预估下载大小;而缺乏客户端超时会导致挂起无限期等待。
立即学习“go语言免费学习笔记(深入)”;
- 务必为
http.Client设置Timeout(如&http.Client{Timeout: 30 * time.Second}) - 若需校验完整性,可同时计算
sha256.Sum256或md5.Hash,在io.Copy过程中注入 hasher - 部分 CDN 或代理可能修改
Content-Length,实际写入字节数应与io.Copy返回值比对
真正容易被忽略的是:即使 io.Copy 成功返回,也不代表磁盘已落盘。如果后续立即校验文件哈希或执行依赖该文件的操作,建议加 out.Sync()(尤其在关键场景下)。










