Iris.Context.Write 不能直接实现 Chunked 传输,因其依赖手动设置 Transfer-Encoding: chunked、禁用 Gzip、提前写 header 并调用 ResponseWriter().Flush() 分批刷出;HTTP/2 下 Flush 无效,SSE 场景推荐使用 iris.Stream。

为什么 iris.Context.Write 不能直接实现 Chunked 传输
因为 Iris 默认使用 http.ResponseWriter 的底层写入机制,而 Go 标准库的 ResponseWriter 在调用 Write 前若未设置 Content-Length 且未显式启用分块,不会自动切换为 Transfer-Encoding: chunked —— 它可能缓冲、截断,甚至在写入中途 panic(比如已写 header 后再改状态码)。关键点在于:Chunked 不是“多调几次 Write”就能触发的,它依赖响应头控制和底层连接状态。
必须手动设置 Transfer-Encoding: chunked 并禁用压缩
Iris 默认启用 Gzip 压缩,而压缩中间件会接管写入流,彻底屏蔽 Chunked 行为。你得绕过它,并显式声明传输编码:
- 调用
ctx.ResponseWriter().Header().Set("Transfer-Encoding", "chunked") - 调用
ctx.Gzip(false)或在路由注册时关闭该 handler 的 gzip(推荐用iris.NoGzip中间件) - **不能**再调用
ctx.StatusCode()或ctx.ContentType()之后才设Transfer-Encoding,否则 header 已 flush,设置无效
用 ctx.ResponseWriter().Write 分批写入 + flush
Chunked 的本质是每写一段数据就 flush 一次,让 net/http 自动包成 size\r\n[data]\r\n 格式。Iris 没有封装 Flush() 的快捷方法,必须取原始 ResponseWriter:
ctx.ResponseWriter().Header().Set("Transfer-Encoding", "chunked")
ctx.Gzip(false)
ctx.StatusCode(200)
ctx.ContentType("text/event-stream") // 或你实际需要的 MIME
<p>// 开始写
for i := 0; i < 5; i++ {
data := fmt.Sprintf("event: message\ndata: %d\n\n", i)
ctx.ResponseWriter().Write([]byte(data))
ctx.ResponseWriter().Flush() // 关键:强制刷出当前 chunk
time.Sleep(1 * time.Second)
}注意:Flush() 只在支持的底层连接(如 HTTP/1.1 明文)上生效;HTTP/2 会忽略它,走自己的流控 —— 所以 Chunked 主要用于兼容老客户端或 SSE 场景。
更安全的替代:用 iris.Stream 封装 SSE 流
如果你实际目标是服务端事件(SSE),别硬搞 Chunked。Iris 提供了 iris.Stream,它内部已处理 header、flush、错误中断和连接保活:
ctx.Stream(func(w io.Writer) bool {
for i := 0; i < 5; i++ {
if _, err := fmt.Fprintf(w, "data: %d\n\n", i); err != nil {
return false // 连接断开,停止推送
}
if err := ctx.ResponseWriter().Flush(); err != nil {
return false
}
time.Sleep(time.Second)
}
return true
})这个方式自动设好 Content-Type: text/event-stream 和 Cache-Control: no-cache,还处理了 client disconnect 检测 —— 比裸写 Chunked 更健壮。真要返回纯 Chunked(比如大文件流式导出),务必确认客户端不依赖 Content-Length 做进度计算,且服务端反向代理(如 Nginx)没强制缓存或重写 Transfer-Encoding。


















