Go并发下载文件需先HEAD确认服务器支持Range请求,再按Content-Length合理分块(如4块/100MB),各goroutine用独立Range请求写入文件指定偏移,最后校验总大小与哈希值。

用 Go 语言实现并发下载文件,核心是把一个大文件按字节范围分块,再用多个 goroutine 并发请求各块,最后合并。关键不是“开很多协程”,而是合理分块、处理 HTTP Range 请求、避免竞争和资源耗尽。
1. 确认服务器支持 Range 请求
不是所有服务器都允许分块下载。先发一个 HEAD 请求,检查响应头是否包含 Accept-Ranges: bytes 或 Content-Range 字段:
- 用
http.Head(url)获取响应头 - 若
resp.Header.Get("Accept-Ranges") == "bytes",说明支持 - 若不支持,只能单连接下载,强行分块会返回 200 + 全体内容,造成重复和错乱
2. 获取文件总大小并计算分块策略
用 HEAD 请求拿到 Content-Length,再按需切分。例如 100MB 文件,开 4 个 goroutine,每块约 25MB:
- 块数不宜过多(如超过 16),否则 TCP 连接、DNS、系统 fd 数可能成为瓶颈
- 每块建议 ≥ 1MB,太小会增加 HTTP 开销;≤ 50MB,避免单 goroutine 内存压力过大
- 计算每个块的
start和end(注意 end 是闭区间,HTTP Range 是 inclusive)
3. 并发发起 Range 请求并写入临时文件
每个 goroutine 独立发起 GET 请求,设置 Range: bytes=start-end 头,并将响应 Body 写入文件指定偏移位置:
立即学习“go语言免费学习笔记(深入)”;
- 用
os.OpenFile(..., os.O_CREATE|os.O_RDWR)打开文件,**不要用 O_TRUNC** - 每个 goroutine 创建独立的
*os.File或共享 file +file.WriteAt()(线程安全) - 务必设置超时:
http.Client{Timeout: 30 * time.Second} - 捕获错误(如 416、网络中断),记录失败块,支持重试(最多 2 次)
4. 合并与校验(可选但推荐)
所有块写完后,无需“合并”操作——因为已按偏移写入同一文件。只需做两件事:
- 检查文件大小是否等于预期
Content-Length,不等说明某块写漏或截断 - 可选:计算最终文件的 SHA256,对比服务端提供的
ETag或单独提供的 checksum - 若出错,只重下失败块,不用重下全部
注意:别用 ioutil.ReadAll + 内存拼接,大文件会 OOM;别用 channel 收集所有数据再写盘,失去流式优势;别在 goroutine 里用同一个 http.Client 不加限制,应复用 Transport 并设 MaxIdleConns。










