断点续传在 Gin 中需服务端真实响应 206、客户端精准 Seek 写入、状态持久化校验三者缺一不可;服务端须手动处理 Range 头并用 http.ServeContent 响应,客户端禁用 os.O_APPEND 改用 WriteAt,状态须独立 .state 文件原子存储。

断点续传在 Gin 中不是开箱即用的功能,必须客户端和服务端严格协同:服务端要真实响应 206 Partial Content 并返回合法 Content-Range,客户端必须绕过 os.O_APPEND、用 Seek 精准写入,且双方状态需持久化校验。缺一不可。
服务端必须显式支持 Range 请求,不能依赖默认 FileServer
Gin 默认路由不解析 Range 头,http.FileServer 或 gin.Static 虽默认支持,但生产环境往往需要权限控制、路径校验或日志审计——这时就得手写 handler。
- 先用
c.Request.Header.Get("Range")提取请求头,格式应为bytes=12345-(注意末尾横杠) - 调用
os.Stat获取文件总大小,校验范围是否越界;若越界返回http.StatusRequestedRangeNotSatisfiable (416) - 计算偏移量和长度后,用
http.ServeContent流式响应:http.ServeContent(c.Writer, c.Request, filename, modTime, reader),其中reader需包装成支持io.ReadSeeker的对象(如io.NewSectionReader(file, offset, length)) - 务必设置
c.Writer.Header().Set("Accept-Ranges", "bytes"),否则某些客户端(如 curl)会忽略续传逻辑 - 别用
c.Data()直接写原始字节——它不自动设Content-Range,也不处理 416
客户端写入必须禁用 os.O_APPEND,用 Seek + WriteAt 替代
os.O_APPEND 是断点续传最隐蔽的坑:它会让所有 Write() 忽略 Seek() 结果,强制追加到末尾,导致数据错位或中间填充空白。
- 打开文件用
os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644),**不带O_APPEND,也不带O_TRUNC** - 每次写入前,必须显式
f.Seek(offset, io.SeekStart),并检查返回值是否等于offset;不等说明文件被外部修改过,应中止续传 - 更稳妥的方式是
f.WriteAt(data, offset),它不依赖当前文件偏移,可精准写入任意位置 - 避免用
bufio.Writer包裹文件——缓冲会破坏Seek和实际落盘位置的一致性 - 写完一块后立即调用
f.Sync(),防止系统缓存导致断电丢数据
断点状态必须独立持久化,不能只靠文件大小判断
仅靠 os.Stat().Size() 判断已下载字节数极不可靠:文件可能被手动截断、覆盖,或因写入失败而残留脏数据。
立即学习“go语言免费学习笔记(深入)”;
- 为每个下载任务生成独立的
.state文件,如video.mp4.download.state,内容为纯文本:offset=12345678\nurl=https://example.com/video.mp4 - 启动时先读
.state,再用os.Stat()校验目标文件大小是否匹配;不匹配则清空重下 - 更新状态必须原子操作:
os.WriteFile(statePath, []byte(content), 0644),而非os.Create+Write,避免中断时留下半截状态 - 每次成功写入后立即更新
.state,且 URL 字段用于防用户换链接却未删旧状态文件 - 下载完成前不要删除
.state,否则中断重启后无法识别续传意图
上线前必须用 curl 实测服务端 Range 行为
Nginx、CDN 或某些反向代理常伪造 Accept-Ranges: bytes 响应头,但实际对 Range 请求返回 200 OK 全量内容——客户端会误以为续传成功,实则从头覆盖写入。
- 用
curl -I -H "Range: bytes=0-1023" https://your-api/file.zip发试探请求 - 只当响应码为
206且Content-Range头匹配(如bytes 0-1023/12345678)才启用续传逻辑 - 若返回
200,说明服务端不支持,应走全量下载路径 - 若返回
416,需先发HEAD拿到真实Content-Length再重试 - Range 值不能有多余空格或非法字符,例如
bytes= 1024 -或bytes=1024-后多一个横杠都会被部分服务端拒绝
真正可靠的断点续传,不在客户端“能不能发”,而在服务端“有没有真回 206”、客户端“有没有真写对位置”、状态“有没有真存住”。三者任一环节松动,就会出现静默错位或重复下载——这些细节,上线前必须逐条验证。


















