go实现断点续传需手动构造range请求头并校验206响应与content-range,文件写入须用seek而非o_append,进度存独立.resume文件并原子更新,恢复时校验url一致性。

Go 本身不提供「断点续传」的封装函数,io.Copy 也不支持偏移控制;真正能落地的方案,是手动管理 file.Seek + http.Request 的 Range 头 + 独立的进度存储文件。
HTTP 客户端如何发 Range 请求并校验 206 响应
服务端必须返回 206 Partial Content,且带 Content-Range 头,仅看状态码不够——比如 CDN 缓存了 200 响应,实际没开 Range 支持,就会 silently 错位写入。
- 用
http.NewRequest构造请求,手动加 header:req.Header.Set("Range", "bytes=1024-") - 收到响应后先检查
resp.StatusCode == http.StatusPartialContent,再解析resp.Header.Get("Content-Range"),例如"bytes 1024-9999/10000" - 提取起始偏移(这里是
1024),与本地已写入字节数比对;不一致说明服务端文件被替换或重置,应清空目标文件并重下 - 不要依赖
Content-Length推算偏移——它只表示本次响应体长度,不是全局位置
本地文件写入必须用 Seek,不能用 O_APPEND
os.O_APPEND 会让内核自动把写位置挪到文件末尾,完全绕过你维护的 offset,导致续传时覆盖已有内容或写到错误位置。
- 打开目标文件用
os.O_WRONLY | os.O_CREATE,**不要加os.O_APPEND** - 每次写前调用
file.Seek(offset, io.SeekStart),确保写入起点精确 - 写完一批数据后立即调用
file.Sync(),防止系统页缓存未刷盘,断电就丢进度 - 多 goroutine 并发写时,offset 更新要用
atomic.StoreInt64,避免竞态覆盖
断点信息必须独立存为 .resume 文件
把 offset 写进目标文件头部会污染原始数据,存在内存里进程一挂就全丢——最简可靠方式是同名 + .resume 后缀的纯文本文件。
- 例如下载
video.mp4,对应断点文件是video.mp4.resume,内容格式:offset=12345\nurl=https://example.com/video.mp4 - 每次成功写入后,用
ioutil.WriteFile(或os.WriteFile)**全量覆盖**该文件,避免部分写失败导致脏数据 - 启动时优先读
.resume,校验其中url字段是否与当前任务一致——防止用户换了下载链接但没删旧 resume 文件 - 下载完成时,记得
os.Remove掉.resume,否则下次可能误判为未完成
真正容易被忽略的是服务端行为一致性:有些 Nginx 配置默认不返回 Content-Range,有些 CDN 对 Range 请求 fallback 到 200;上线前务必用 curl -v -r 1024- https://... 实测响应头。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











