go下载远程文件不能仅用http.get,需按协议选方案:http用自定义超时和状态码校验的http.client,ftp用jlaffaye/ftp,sftp用pkg/sftp。

Go 语言下载远程文件,不能只靠 http.Get 一把梭——它没超时、不校验状态码、不管理 Cookie、不处理重定向上下文,大文件直接 OOM,403/302/502 全当成功。必须分协议选方案:HTTP 用 http.Client 手动控制;FTP 用 github.com/jlaffaye/ftp;SFTP 用 github.com/pkg/sftp;别混用,也别硬套默认行为。
HTTP 下载必须手动设超时和检查 StatusCode
默认 http.Get 没超时,网络卡住就永远 hang 住;返回 404 或 502 却照常写入空文件或错误页,根本不是“下载失败”,而是“下错了”。
- 用
&http.Client{Timeout: 30 * time.Second}显式设超时,别依赖http.DefaultClient - 调
resp, err := client.Do(req)后,立刻检查resp.StatusCode:只接受200 ,302 要自己判断是否需带 Cookie 重放 -
defer resp.Body.Close()必须在 StatusCode 检查之后,否则 4xx/5xx 的 body 也要关,不然连接复用会出问题 - 别用
io.ReadAll(resp.Body),大文件必崩;用io.Copy(dstFile, resp.Body)流式落盘
下载前不加 User-Agent 和 Accept 头,大概率 403 或返回 HTML
GitHub raw、Cloudflare、多数 CDN 和静态托管(Vercel/Netlify)默认拦截无头请求。返回 403 不是权限问题,是服务端压根没认出你是“客户端”。
- 必须手动设置:
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") - 下载二进制文件(如 ZIP、PDF、PNG)时,加
req.Header.Set("Accept", "application/octet-stream"),否则可能返回登录页 HTML - 若目标需登录态(如 Chess.com PGN 下载),还得配
http.CookieJar,并禁用自动重定向(CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }),自己带 Cookie 重发
FTP 和 SFTP 协议不能用 net/http,得换专用库
Go 标准库压根不支持 FTP/SFTP。用 http.Get("ftp://...") 会 panic:no such host,因为 net/http 只认 HTTP/HTTPS。
- FTP 下载:用
github.com/jlaffaye/ftp,连上后conn.Retr(remotePath)返回io.ReadCloser,再io.Copy(file, reader) - SFTP 下载:用
github.com/pkg/sftp+golang.org/x/crypto/ssh,client.Open(remotePath)得到fs.File,调file.WriteTo(dstFile)或io.Copy - 两者都需显式关闭连接:
defer conn.Quit()(FTP)或defer client.Close()(SFTP),漏掉会导致 fd 耗尽 - SSH 连接务必设
HostKeyCallback:测试可用ssh.InsecureIgnoreHostKey(),但生产必须校验公钥,否则中间人攻击无感
断点续传不是默认能力,得自己验证 Accept-Ranges 并构造 Range
不是所有 HTTP 服务支持断点续传。Range 请求发出去,服务端不认,照样返回 200 + 全量内容,你却以为续上了。
- 先发
HEAD请求,检查响应头:resp.Header.Get("Accept-Ranges") == "bytes" - 确认支持后,再发
GET,设req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset)),offset必须等于本地文件当前长度(用os.Stat获取) - 收到响应后,必须检查
resp.StatusCode == 206,否则就是服务端忽略 Range,得从头下 - 写入时用
os.OpenFile(path, os.O_WRONLY|os.O_APPEND),别用os.Create覆盖
最易被跳过的其实是 StatusCode 校验和 Body.Close() 时机——它们不出错不报警,但会让下载逻辑在 404、502、302 场景下静默失败,日志里只有一行“文件已保存”,而内容是 HTML 登录页或空。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











