流式响应必须手动调用http.Flusher.Flush()才能实时推送,因ResponseWriter默认缓冲数据;需先类型断言再刷新,且要禁用干扰中间件、设置合适响应头并监控客户端断连。

流式响应必须手动控制http.ResponseWriter的写入时机
Gin 默认会缓冲整个响应体,直到 handler 返回才一次性写出。要实现流式渲染(比如边生成 PDF/CSV/MP4 边发给客户端),必须绕过 Gin 的默认写入逻辑,直接操作底层的 http.ResponseWriter,并调用 Flush() 强制推送已写数据。
常见错误是只调用 Write() 但没 Flush(),导致浏览器一直等待“结束”,实际数据卡在服务端缓冲区里。
- 务必在每次写入后调用
rw.(http.Flusher).Flush(),且需先断言类型:if f, ok := c.Writer.(http.Flusher); ok { f.Flush() } - 禁用 Gin 的中间件自动写入(如
gin.Recovery()可能干扰流式响应),建议在流式 handler 中显式关闭日志记录或使用独立路由组 - 设置合适的 HTTP 头:
c.Header("Content-Type", "application/octet-stream")、c.Header("Content-Transfer-Encoding", "binary"),避免代理或浏览器做额外解码
大文件流式传输要防阻塞和超时
Gin 的默认 ReadTimeout 和 WriteTimeout(通常为 0,即不限)在流式场景下反而危险——如果下游网络卡顿或客户端暂停接收,goroutine 会长期挂起,最终耗尽连接池或触发系统级超时(如 Nginx 的 proxy_read_timeout)。
不能依赖框架默认行为,得主动管理生命周期。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 对长时流式响应,建议在 handler 内部启动带超时的 goroutine 控制写入节奏,例如用
time.AfterFunc()触发中断清理 - 用
c.Request.Context().Done()监听客户端断连,一旦收到context.Canceled就停止生成和写入 - 若用
io.Copy()或io.CopyN()转发文件,务必包装源 reader(如io.LimitReader())并检查返回错误,防止因 EOF 或 I/O 错误卡死
不同协议文件的流式处理要点差异
不是所有文件格式都适合“边生成边发送”。关键看生成逻辑是否支持增量输出,以及客户端是否能正确解析未完成的数据流。
例如 CSV 可逐行写入;PDF 必须用支持流式生成的库(如 unidoc 或 gofpdf 的 AddPage() + Output() 分段);而 MP4 需满足 moov box 在前或使用 fragmented MP4(fMP4)格式,否则播放器无法初始化。
-
CSV:用csv.NewWriter(c.Writer),每写一行后调用w.Flush(),无需预计算总大小 -
PDF:避免gofpdf.New()后直接Output()全量,改用gofpdf.NewCustom(&gofpdf.InitType{Unit: "pt", PageSize: gofpdf.Rect{W: 595.28, H: 841.89}})+ 手动分页 + 每页后f.Flush() -
MP4:优先选用mp4.Fragmented()模式生成,或用ffmpeg -f mp4 -movflags +frag_keyframe+empty_moov输出流式兼容格式,服务端仅做io.Copy()转发
客户端接收流式响应时容易忽略的兼容性问题
浏览器原生 fetch() 对流式二进制响应支持不一致:Chrome 支持 response.body.getReader(),Safari 16.4+ 才完整支持 ReadableStream,IE 完全不支持。移动端 WebView 更混乱。
别假设前端能“自动处理”流式内容。
- 不要依赖
Content-Disposition: attachment自动下载——某些 iOS WebView 会直接丢弃未完整接收的文件 - 推荐搭配简单进度提示:服务端在流开头写入 JSON 前缀(如
{"status":"start","total":1048576}),再接二进制数据,前端用TextDecoder分离元信息与 payload - 若需强兼容,退化方案是用
X-Accel-Redirect(Nginx)或X-Sendfile(Apache)交由 Web 服务器处理静态文件流,Gin 只负责鉴权和路径生成

















