17370845950

如何使用Golang实现文件下载_Golang net/http GET与io Copy方法
必须检查响应状态码,否则可能将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 main

import ( "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 {
    pa

nic(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.Sum256md5.Hash,在 io.Copy 过程中注入 hasher
  • 部分 CDN 或代理可能修改 Content-Length,实际写入字节数应与 io.Copy 返回值比对

真正容易被忽略的是:即使 io.Copy 成功返回,也不代表磁盘已落盘。如果后续立即校验文件哈希或执行依赖该文件的操作,建议加 out.Sync()(尤其在关键场景下)。