iris 框架导出 excel 必须用 excelize/v2 配合流式响应,禁用中间件封装、手动控制 write/flush、使用 streamwriter 分批写入,并严格按序设置字体、时间格式与 rfc 5987 编码文件名。

iris 框架本身不内置 Excel 生成能力,导出必须靠 excelize/v2 配合 HTTP 流式响应实现;直接用 ctx.JSON() 或 ctx.ServeFile() 都会失败或触发乱码、卡顿、文件名丢失。
为什么 Iris 的 ctx.ResponseWriter() 不能直接 WriteTo()?
iris.Context.ResponseWriter() 是封装过的 http.ResponseWriter,但部分中间件(如 gzip、logger)可能已对其包装,导致底层 http.Flusher 或 http.Hijacker 接口不可用。而 excelize.File.WriteTo() 依赖原始 io.Writer 的稳定写入能力,一旦被中间件劫持缓冲,就会出现:
- 前端下载文件体积为 0 字节
- Chrome 报 net::ERR_INCOMPLETE_CHUNKED_ENCODING
- Safari 完全无响应(Blob 构造失败)
正确做法是绕过中间件封装,用 ctx.ResponseWriter().Write() + ctx.ResponseWriter().Flush() 手动控制流:
诊断并修复涉及公式、命名范围、Power Query 刷新、数据透视表、VBA/宏保存等问题的 Microsoft Excel XLSX 工作簿
- 先调用
ctx.ContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") - 再设置标准响应头:
ctx.Header("Content-Disposition", `attachment; filename="report.xlsx"`) - 禁用自动压缩:
ctx.Header("Content-Transfer-Encoding", "binary")和ctx.Header("Cache-Control", "no-cache, no-store, must-revalidate") - 创建
excelize.File后,用f.Write()写入ctx.ResponseWriter(),**不要用f.WriteTo()** ——后者内部会尝试调用Flush(),但在 Iris 封装下易 panic
流式写入必须用 StreamWriter,不能 NewFile().SetCellValue()
1000 行以上数据若用SetCellValue() 全量构建内存模型,*excelize.File 实例会暴涨至百 MB,且 f.Write() 阻塞期间整个 goroutine 卡死,Iris 并发请求数迅速跌穿阈值。
必须切换到流式路径:
- 调用
f.NewStreamWriter("Sheet1")获取*excelize.StreamWriter - 表头用
sw.SetRow("A1", headers)一次性写入 - 每批数据(建议 500–2000 行)调一次
sw.SetRow(fmt.Sprintf("A%d", row), rowData),然后立即sw.Flush() - 写完必须
sw.Close(),否则最后几行丢失 - 最后调
f.Write(ctx.ResponseWriter())——注意:此时f已含完整 ZIP 结构,Write()只做序列化输出
中文乱码、时间变数字、文件名失效的三连坑
这三个问题在 Iris 中高频共现,根源都在响应头与 excelize 初始化顺序错位:-
中文乱码:不是编码问题,是字体缺失。必须在
f.NewStreamWriter()前执行f.SetFontFamily("Sheet1", "Microsoft YaHei");若用流式,还要在sw.SetRow()后对首行手动f.SetCellStyle("Sheet1", "A1", "Z1", styleID) -
时间字段显示为 45201.625:不能直接
SetCellValue("C2", time.Now())。要先转 Excel 序列值:excelize.TimeToExcelTime(t, false),再配合f.SetCellNumFormat("Sheet1", "C2", "yyyy-mm-dd hh:mm:ss") -
文件名变成 download.xlsx 或乱码:Iris 默认不处理
Content-Disposition中的 UTF-8 文件名。必须用 RFC 5987 编码:filename*=UTF-8''report-%E6%95%B0%E6%8D%AE.xlsx,其中%E6%95%B0%E6%8D%AE是 “数据” 的 UTF-8 URL 编码
流式导出真正的难点不在代码行数,而在每个环节的“时序敏感性”:字体设早了没用,设晚了已写入的内容不生效;Flush() 调太勤拖慢吞吐,调太懒内存涨爆;Content-Disposition 缺一个单引号,Safari 就拒绝解析。这些细节没有银弹,只能靠压测+抓包+逐行比对 response body 验证。










