如何使用Golang并发下载文件_协程分块请求加快速度

8次阅读

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

如何使用 Golang 并发下载文件_协程分块请求加快速度

用 Go 语言实现并发下载文件,核心是把一个大文件按 字节 范围分块,再用多个 goroutine 并发请求 各块,最后合并。关键不是“开很多协程”,而是合理分块、处理 HTTP Range 请求、避免竞争和资源耗尽。

1. 确认服务器支持 Range 请求

不是所有服务器都允许分块下载。先发一个 HEAD 请求,检查响应头是否包含 Accept-Ranges: bytesContent-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 内存压力过大
  • 计算每个块的 startend(注意 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。

text=ZqhQzanResources