http.FileServer默认支持断点续传,因Go 1.16+已内置Range请求处理;但若启用gzip中间件、使用不支持Seek的自定义文件系统或误配路由,则会导致206响应失效。

为什么 http.FileServer 默认不支持断点续传
因为 http.FileServer 底层用的是 http.ServeContent,但它只在响应头中写入 Content-Length 和基础 MIME 类型,不主动解析 Range 请求头、不设置 Accept-Ranges: bytes、也不返回 206 Partial Content。浏览器或下载工具发起带 Range: bytes=100-199 的请求时,它直接回 200 OK 全量内容,导致客户端认为服务端不支持范围请求而放弃续传。
用 http.ServeContent 手动处理范围请求
必须自己写 handler,接管文件读取逻辑,显式调用 http.ServeContent 并传入满足 http.File 接口的自定义对象(比如 os.File)。关键点在于:文件必须可随机读(Seek)、响应头要提前设置 Content-Type 和 Content-Length、且不能提前写 body。
-
http.ServeContent会自动检查Range请求头,决定返回200还是206,并正确设置Content-Range、Accept-Ranges - 必须用
file.Stat()获取文件大小,传给http.ServeContent的modtime参数建议用stat.ModTime(),否则 ETag 和缓存行为可能异常 - 不要在调用
http.ServeContent前写任何响应体,否则会 panic:“http: superfluous response.WriteHeader call”
func serveFile(w http.ResponseWriter, r *http.Request) {
path := "/path/to/file.zip"
f, err := os.Open(path)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
defer f.Close()
stat, _ := f.Stat()
w.Header().Set("Content-Type", "application/zip")
http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
}
如何让 http.FileServer 支持范围请求(绕过重写 handler)
可以包装 http.FileSystem,让 Open 返回的 http.File 实现 Stat() 和 Seek(),再配合 http.FileServer 自身逻辑——但注意:Go 1.16+ 的 http.Dir 已内置对 Range 的支持,只要文件系统返回的 http.File 支持 Seek 即可生效。
- 默认
http.Dir("/static")在 Go 1.16+ 中已能正确响应Range请求,无需额外代码 - 若用自定义
http.FileSystem(如从 zip 内部读文件),需确保Open返回的对象同时实现http.File和io.Seeker - 验证是否生效:用
curl -H "Range: bytes=0-1023" http://localhost:8080/big.zip -I,看响应头是否有HTTP/1.1 206 Partial Content和Content-Range
常见踩坑点:gzip 压缩与范围请求冲突
如果启用了 gzip 中间件(如 golang.org/x/net/http2 自动启用,或手动加 compress/gzip handler),会导致 Range 失效——因为压缩后文件长度不可预知,Content-Length 和 Content-Range 无法正确计算。
- HTTP/2 下,服务器必须禁用对范围请求的 gzip 压缩;Go 标准库的
http.Server在Range存在时会自动跳过gzip(前提是没被第三方中间件干扰) - 如果你用了自定义压缩中间件,务必在
if r.Header.Get("Range") != ""时跳过压缩逻辑 - 用
curl -H "Accept-Encoding: gzip"测试时,记得同时加Range,否则容易误判为“压缩生效”
Range,多试几种组合:bytes=0-、bytes=-1024、bytes=100-199,300-399(多段范围),标准库只支持单段,多段会退回到 200,这点容易被忽略。

















