脚本要从 CDN 拉一个几百 MB 的安装包,图省事用了 ReadAll 把响应全读进内存,2G 内存的机器当场被撑爆。Go 里下文件应该流式拷贝,边读边写。
最短能跑的版本
步骤就四步:发请求、看状态码、建文件、拷贝。每行的 err 都要处理,Create 失败还继续 Copy 会直接 panic。
resp, err := http.Get(url)defer resp.Body.Close()f, err := os.Create(filepath.Join(dir, name))_, err = io.Copy(f, resp.Body)io.Copy 内部用 32KB 缓冲循环读写,内存占用和文件大小无关。想显示进度就换成 io.CopyBuffer 加一个自己实现的 Writer,在 Write 里累加字节数。
http.Get 默认没有超时
它用的是 DefaultClient,不设超时,对方半死不活挂着连接,程序就一直等。生产代码自己建 client:&http.Client{Timeout: 10 * time.Minute},大文件按体积给,别照抄示例里的 30 秒。要更细就控 Transport 的 DialContext 和 ResponseHeaderTimeout。
文件名不能直接拼
URL 里的名字先用 path.Base 取,再过滤掉 .. 和空串,否则一个构造过的链接就能把文件写到上级目录。同名文件用 OpenFile 配 O_CREATE|O_EXCL,或者先写 .part 临时文件,成功后再 rename,中途断网不会留下半个坏文件骗过校验。
断点续传
请求头加 Range: bytes=已下载字节-,服务端支持时返回 206,接着往文件尾部追加。必须判断返回码,206 才追加;返回 200 说明对方忽略了 Range,得清空重下,否则文件里会重复一段。
顺手校验一下大小
响应头里的 Content-Length 是字节数,拷贝完对一下实际写入量,能提前发现被截断的下载。CDN 开了 gzip 时这个值会和落盘大小对不上,判断前先看 Content-Encoding。要求完整性的场景,服务端一般会给摘要,下完算一次 sha256 比对最稳。
核心就两点:流式别读内存、检查状态码。超时和临时文件算可选项,但凡是要上线的下载逻辑,最好一次写全。