gin 的 c.file() 不支持 range 请求,需用 c.datafromreader() 手动解析 range 头并调用 http.servecontent;反向代理时须显式透传 range 头;断点续传需客户端携带 offset 或先发 head 获取 accept-ranges;调试必须用 curl 验证 206 响应。

服务端用 Gin 返回静态文件时,必须显式支持 Range 请求
Gin 的 c.File() 默认不处理 Range 头,即使你本地文件存在、客户端发了 bytes=1000-,它也只返回 200 和完整文件体,根本不会切片——这不是 bug,是设计如此。真正能自动响应 206 Partial Content 的,只有标准库的 http.ServeContent 或显式调用 http.ServeFile(需配合 http.FileServer)。所以别指望 c.File() 自动续传。
可行路径只有一条:用 c.DataFromReader() + 手动解析 Range 头 + 调用 http.ServeContent。但注意:http.ServeContent 需要一个实现了 io.Seeker 的 reader,普通 os.File 可以,bytes.Reader 不行。
- 先用
os.Open()打开文件,得到*os.File - 用
req.Header.Get("Range")提取范围,再调用http.ParseRange()解析(Go 1.16+ 内置) - 若解析成功且范围合法,用
http.ServeContent(c.Writer, c.Request, filename, modtime, file) - 否则 fallback 到全量返回(
c.DataFromReader(200, ...))
客户端发起 Range 请求时,Gin 接口必须保留原始请求头
如果你把 Gin 当作反向代理(比如前端请求 /download → Gin 转发到后端文件服务),那默认的 http.Client.Do() 会丢掉 Range 头。Gin 本身不做透传,必须手动复制:
req.Header.Set("Range", c.Request.Header.Get("Range")) —— 这行不能漏,而且得在 http.NewRequest 之后、Do() 之前执行。
常见错误现象:curl -H "Range: bytes=5000-" http://localhost:8080/download 返回 200 全量内容,但 curl -I 看不到 Accept-Ranges: bytes 或 Content-Range,说明请求头没透传过去,或者后端压根没收到。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 检查 Gin 中间件是否修改或清空了
Header(比如某些日志中间件会 clone request) - 确保转发目标服务支持 Range(Nginx 默认支持,但需确认未配置
add_header Accept-Ranges "";) - 不要用
c.Redirect(),它会丢失所有自定义 header
本地文件路径校验与断点逻辑必须解耦
很多人把“文件存在”和“可续传”混为一谈。Gin 接口收到请求时,os.Stat() 查到文件存在 ≠ 客户端上次下载到了哪——因为客户端可能中断在任意字节,而文件系统只告诉你当前长度,不告诉你“这段数据是否完整校验过”。所以不能仅靠 fi.Size() 就直接塞进 Range 头。
安全做法是让客户端自己携带断点位置(如 query 参数 ?offset=12345),服务端只做白名单校验和范围截断;或者用更健壮的方式:客户端先发 HEAD,服务端在响应头里返回 Content-Length 和 Accept-Ranges: bytes,客户端再决定是否发 Range。
- 绝对禁止把用户输入的
offset直接拼进Range值而不校验是否越界 - 若
offset >= file.Size(),应返回 416Requested Range Not Satisfiable,而不是静默返回空响应 - Windows 下注意
os.Stat()对长路径或符号链接可能返回错误,建议加filepath.EvalSymlinks()预处理
调试时 curl 是唯一可信的验证工具
浏览器下载行为不可信:它可能缓存了旧响应、忽略 Range、或自动 fallback 到全量。真正验证断点是否生效,只能靠 curl 命令逐层排查:
curl -I -H "Range: bytes=0-1023" http://localhost:8080/static/file.zip —— 看是否返回 HTTP/1.1 206 Partial Content 和 Content-Range: bytes 0-1023/1048576
curl -H "Range: bytes=1024-2047" http://localhost:8080/static/file.zip | wc -c —— 应输出 1024,不是 0 或 1048576
- 如果第一步返回 200,说明 Gin 没走
http.ServeContent路径,或文件打开失败回退到了全量逻辑 - 如果第二步输出异常,检查
os.File是否被提前Close(),或http.ServeContent的modtime参数传了零值(会导致 304 缓存误判) - 别依赖 Postman 的“Download”按钮,它底层行为不透明;一定要用命令行原始响应
curl -v 抓包确认。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










