Go实现HTTP断点续传需手动设置Range头如"bytes=1024-",服务端须返回206及Content-Range;写文件用Seek定位而非O_APPEND;断点存.state文件并校验大小;遇416时发HEAD获取最新长度比对。

Go 怎么用 http.Range 请求文件分片
断点续传本质是让客户端告诉服务端“我要从第 N 字节开始下载”,这靠 HTTP 的 Range 请求头实现。Go 的 http.Client 本身不自动处理断点,但可以手动设置:req.Header.Set("Range", "bytes=1024-")。注意:服务端必须返回 206 Partial Content,否则说明它不支持范围请求——常见于静态文件服务器未启用 Accept-Ranges: bytes,或 Nginx/Apache 配置缺失。
关键细节:
-
Range值格式必须严格为bytes=start-end(end 可省略表示“到结尾”),不能多空格、不能写成bytes=start-带多余横杠 - 服务端返回的
Content-Range头需解析出真实总长度,用response.Header.Get("Content-Range")提取后正则匹配,例如bytes 1024-9999/10000中的10000 - 如果服务端返回
200 OK而非206,说明 Range 被忽略,此时应停止续传逻辑,改用全量下载
怎么安全地追加写入已存在的文件
用 os.OpenFile 打开文件时,必须同时指定 os.O_WRONLY | os.O_CREATE | os.O_APPEND 是错的——O_APPEND 会强制写到文件末尾,无法控制偏移量。正确做法是用 os.O_WRONLY | os.O_CREATE,再调用 file.Seek(offset, io.SeekStart) 定位,最后 file.Write(data)。
容易踩的坑:
立即学习“go语言免费学习笔记(深入)”;
- 没检查
Seek返回值,若磁盘满或文件被删,Seek可能失败但后续Write仍执行,导致数据错位 - 并发写同一文件时没加锁,多个 goroutine 同时
Seek+Write会相互覆盖;建议用sync.Mutex包裹写操作,或直接用单 goroutine 顺序写 - 写入前没用
os.Stat确认文件已存在且大小 ≥ offset,否则可能覆盖已有内容
如何持久化断点位置并防重复记录
断点信息不能只存在内存里,得落地。最轻量方式是把当前已下载字节数写进一个同名的 .download.state 文件(如 foo.zip.download.state),内容就一行纯数字。每次启动先读这个文件,再校验原文件大小是否匹配该数值——不匹配说明文件被手动修改过,应清空状态重下。
注意点:
- 写状态文件必须用
os.WriteFile(原子替换),别用os.Create + Write,否则中断时留下半截状态 - 读状态前先
os.Stat检查状态文件是否存在,不存在就视为首次下载,offset = 0 - 不要把状态存进数据库或复杂配置,断点续传场景下,简单即可靠
遇到 416 Range Not Satisfiable 怎么办
这个错误意味着你发的 Range: bytes=12345- 超过了服务端文件实际大小。常见于:服务端文件已被更新变小、CDN 缓存了旧的 Content-Length、或你本地记录的 offset 比实际文件大。
应对策略:
- 捕获
416响应后,立刻发一次无Range头的 HEAD 请求,获取最新Content-Length - 对比本地记录的 offset 和新获取的长度:若 offset ≥ 新长度,说明已完成,直接退出
- 若 offset
真正麻烦的是服务端不返回准确 Content-Length(如动态生成的流式响应),这种场景 Go 做断点续传基本不可行,得换协议或放弃续传。


















