Go下载文件需两步:发GET请求并流式写入文件;须用http.Client.Do设超时、检查状态码、defer关闭Body,再用io.Copy写入文件,避免内存溢出。

Go 语言下载文件,核心就两步:发 GET 请求 + 把响应体流式写入文件。别用 http.Get 直接读全部到内存,大文件会崩。
用 http.Client.Do 而不是 http.Get
很多初学者直接用 http.Get,然后调 resp.Body.Read 或 io.ReadAll,这等于把整个文件加载进内存——下个 500MB 的 ISO 就 OOM。必须用 http.Client.Do 手动控制请求,并配合流式写入。
-
http.Get是http.DefaultClient.Get的封装,没法设超时、重试、自定义 Header,也不方便复用连接 - 用
&http.Client{Timeout: 30 * time.Second}显式控制超时,避免卡死 - 拿到
resp.Body后,立刻用io.Copy写入*os.File,不经过中间 buffer
正确处理 resp.Body 和错误链
常见崩溃点:没关 resp.Body、忽略 resp.StatusCode、把 404/502 当成成功下载。
- 必须用
defer resp.Body.Close(),但得在检查resp.StatusCode之后——否则 4xx/5xx 响应体也要关 - 下载前先检查
resp.StatusCode >= 200 && resp.StatusCode ,否则 <code>io.Copy会把错误页内容也写进文件 - 如果服务端返回了
Content-Length,可以提前创建文件并预分配空间(用os.Truncate),但不是必须
带进度提示的下载(io.MultiWriter + io.TeeReader)
终端里干等不知道下到哪了?不用第三方库,标准库就能做进度条。
- 用
io.TeeReader(resp.Body, progressWriter),把每读一块数据都“镜像”给一个计数器 - 进度 writer 可以是自定义 struct,实现
Write(p []byte) (n int, err error),只累加长度不写磁盘 - 最终写文件用
io.Copy(dstFile, teeReader),一次完成读取+计数+落盘 - 注意:不要对
teeReader做多次io.Copy,它只能消费一次
HTTPS 证书问题和代理配置
内网环境或自签名证书下,http.Client 默认拒绝连接,报错 x509: certificate signed by unknown authority。
- 临时绕过(仅测试):设置
Transport.TLSClientConfig.InsecureSkipVerify = true,但生产禁用 - 正确做法:把企业 CA 证书加到系统 cert pool,或用
tls.X509KeyPair加载私有证书 - 走代理时,别只设
HTTP_PROXY环境变量——Go 默认只对http协议生效;HTTPS 需显式配Transport.Proxy = http.ProxyFromEnvironment,它会自动识别HTTPS_PROXY
最易被忽略的是状态码校验和 Body.Close() 的位置关系:先判断状态,再决定是否读 Body,再统一 Close。漏掉这个顺序,小文件看着没问题,一到重定向或服务端异常就静默写入空文件或 HTML 错误页。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











