用http.head检查服务端是否支持断点续传,需发起head请求并验证响应头accept-ranges: bytes;若不存在或返回416,则退回到全量下载。

如何用 http.Head 检查服务器是否支持断点续传
断点续传的前提是服务端支持 Range 请求,否则客户端再怎么实现都无效。最直接的验证方式是发一个 HEAD 请求,看响应头里有没有 Accept-Ranges: bytes 或 Content-Range 字段。
实操建议:
- 用
http.Head(url)发起请求,不要用GET,避免下载完整体 - 检查响应头:
resp.Header.Get("Accept-Ranges") == "bytes"是最常见且可靠的判断依据 - 某些 CDN 或静态文件服务器(如 Nginx 默认配置)会返回
Accept-Ranges: bytes;而 Go 的net/http本地服务默认不支持,需手动处理Range头 - 如果返回
416 Range Not Satisfiable或没有Accept-Ranges,说明不支持,此时应退回到普通下载,别硬上断点逻辑
http.Request 中设置 Range 请求头的正确写法
断点续传本质就是告诉服务器“我要从第 N 字节开始下载”,靠的是 Range 请求头。Go 标准库不会自动加这个头,必须手动设置。
实操建议:
- 格式固定为
"bytes=" + strconv.FormatInt(start, 10) + "-",例如从第 1000 字节开始:"bytes=1000-" - 注意结尾的短横线
-表示“到末尾”,不要写成"bytes=1000-1999"(除非你明确知道文件总长且想下指定区间) - 如果文件已存在,用
os.Stat获取当前大小作为start;若出错(如文件不存在),start设为 0 - 务必在
http.NewRequest后、client.Do前调用req.Header.Set("Range", rangeStr),顺序错了头就丢了
如何安全地追加写入已有文件并校验响应长度
断点续传不是简单打开文件追加,得兼顾原子性、并发安全和响应一致性。尤其要注意:服务器返回的 Content-Length 是本次响应体长度,不是整个文件长度。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
实操建议:
- 用
os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0)打开文件,避免覆盖已有内容 - 读取响应体前,先检查
resp.StatusCode是否为206 Partial Content;如果是200,说明服务器没按Range处理,得清空重下 - 用
io.Copy写入时,建议包装一层带进度回调的写入器,而不是直接丢给io.Copy—— 否则无法感知写入是否中断或偏移错位 - 写完后,用
os.Stat确认文件最终大小是否等于预期(即原大小 + 本次响应体长度),不一致就得删掉重来
并发分块下载时如何避免文件偏移错乱
单纯多 goroutine 下载不同 Range 区间,很容易因写入顺序不可控导致文件错位。这不是 Go 特有问题,而是所有语言做并发断点续传都要面对的底层约束。
实操建议:
- 每个 goroutine 下载前,先用
os.OpenFile以O_WRONLY | O_CREATE模式打开文件,然后用file.Seek(offset, 0)定位到对应起始位置 - 写入必须用
file.Write(而非WriteAt),因为Write会从当前 offset 开始写,且保证原子性;WriteAt在某些文件系统上可能不安全 - 不要依赖 goroutine 启动/完成顺序,所有块的写入 offset 必须由主逻辑严格计算并传入,不能靠“第几个 goroutine 就写第几块”这种模糊约定
- 最后合并前,建议用
sha256对每个块单独校验,比等全部下完再算整体 hash 更早发现问题
真正麻烦的从来不是怎么发请求,而是怎么确保每一块字节都落在它该在的位置——哪怕磁盘缓存、内核调度、goroutine 调度全都不按你写的顺序来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










