应直接用 excelize.newfile() 写入数据后调用 f.write(ctx.responsewriter()),并提前设置 content-type 和 content-disposition 响应头,避免磁盘 i/o 或响应头顺序错误导致下载失败。

用 excelize 生成 Excel 并写入 HTTP 响应体
Iris 本身不内置 Excel 处理能力,得靠第三方库生成文件流。最稳妥的选择是 github.com/xuri/excelize/v2(v2 版本支持 Go 1.16+,无 CGO 依赖)。关键不是“导出”,而是把生成的 Excel 写进 ctx.ResponseWriter(),并设置正确的响应头。
常见错误是先保存到磁盘再读取返回——这多一次 I/O,还可能因并发写同一路径导致混乱。应该直接用 bytes.Buffer 或 io.Pipe 构建内存流:
- 创建
f := excelize.NewFile() - 写入数据(
f.SetCellValue、f.SetSheetRow等) - 调用
f.Write(ctx.ResponseWriter())—— 注意:这是 excelize 自带方法,会自动设置Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet - 手动加
Content-Disposition头让浏览器触发下载:ctx.Header("Content-Disposition", "attachment; filename=report.xlsx")
ctx.Header 的顺序和时机影响下载行为
如果 Excel 文件能生成但浏览器没弹下载框,大概率是响应头写晚了或被覆盖。Iris 中 ctx.Header() 必须在 f.Write() 之前调用,且不能在中间夹杂任何 ctx.WriteString() 或 ctx.JSON() —— 那些会提前触发 HTTP header 发送,后续再设 Content-Disposition 就无效了。
典型踩坑场景:
- 误在
f.Write()后补ctx.Header()→ 浏览器当成 inline 渲染(打不开) - 用
ctx.ServeFile()想绕过生成逻辑 → 只适合静态文件,无法动态填充数据 - 没设
filename或含中文没 URL 编码 → Chrome 下载名变乱码,Safari 直接失败
中文文件名建议用 url.PathEscape("报表_" + time.Now().Format("20060102") + ".xlsx"),然后拼进 Content-Disposition。
大数据量下避免内存爆掉
用 excelize 往内存里塞几万行,bytes.Buffer 占用可能飙升到百 MB。此时别硬扛,改用流式写法:
- 初始化
pr, pw := io.Pipe() - 起 goroutine 调
f.Write(pw)(注意 recover panic) - 主协程调
io.Copy(ctx.ResponseWriter(), pr)
这样 Excel 是边生成边传输,峰值内存只取决于单行数据结构大小。不过要注意:一旦开始 io.Copy,就不能再修改响应头,所以 Content-Disposition 和 Content-Type 必须提前设好。
前端触发下载要配对 responseType: "blob"
后端逻辑跑通不代表前端能下——如果用 fetch 或 axios 请求,必须显式设 responseType: "blob",否则浏览器会尝试解析成文本,报 Unexpected token in JSON at position 0 这类错。
示例 fetch 调用:
fetch("/api/export", { method: "GET" })
.then(res => res.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "report.xlsx";
a.click();
URL.revokeObjectURL(url);
});
漏掉 res.blob() 或没用 URL.createObjectURL 包装,都会导致下载失败或文件损坏。
Excel 生成本身不难,难的是头和体的配合、流与内存的权衡、前后端 responseType 的严格对应——任一环松动,下载就静默失败。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











