必须检查响应状态码,否则可能将404、500等错误响应误作文件内容写入磁盘,导致损坏或空文件;应先判断resp.StatusCode >= 200 && resp.StatusCode用
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 mainimport ( "io" "net/http" "os" )
func main() { resp, err := http.Get("https://www./link/de829cc41d27f07c17771b5027167353") if err != nil { panic(err) } defer resp.Body.Close()
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 判断结束),但无法预估下载大小;而缺乏客户端超时会导致挂起无限期等待。
- 务必为
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()(尤其在关键场景下)。