根本原因是响应头顺序错乱或缺失关键字段,必须先设content-type和content-disposition头,再写bom及csv内容,并显式调用flush()触发浏览器下载。

Beego 中导出 CSV 文件不下载只显示内容?
根本原因是响应头顺序错乱或缺失关键字段。Beego 的 Ctx.ResponseWriter 是底层 http.ResponseWriter,但若在调用 Write 后才设 header,浏览器已按默认 MIME 类型渲染文本,不会触发下载。
必须严格按以下顺序操作:
- 先调用
Ctx.Output.Header("Content-Type", "text/csv; charset=utf-8") - 再设
Content-Disposition:例如Ctx.Output.Header("Content-Disposition", "attachment; filename=\"data.csv\"") - 最后写入 BOM + CSV 内容,并显式调用
Ctx.ResponseWriter.(http.Flusher).Flush()
漏掉 Flush() 是高频坑点 —— Beego 默认缓冲响应体,不强制刷出会导致部分浏览器(尤其 Chrome)静默截断。
导出 Excel(.xlsx)必须用 excelize/v2,别碰 encoding/csv“伪 Excel”
CSV 文件双击能被 Excel 打开,但不是 Excel 文件:encoding/csv 无法写入日期格式、单元格样式、合并单元格、多工作表,且字段含逗号或换行时若手动拼接极易错列。
真实 Excel 导出应走 github.com/xuri/excelize/v2,注意三点:
- 安装命令必须带
/v2:go get github.com/xuri/excelize/v2,旧版excelize(无版本后缀)已归档,不兼容新 Excel 格式 - 中文标题/内容无需转码,但需显式设置字体避免某些 Linux 环境下显示为空:用
f.SetCellStyle("Sheet1", "A1", "C1", styleID)配合f.NewStyle - 大数据量(>5 万行)别用
SetCellValue逐单元格写,改用SetSheetRow("Sheet1", "A1", &rows)批量写入二维切片,性能高 5 倍以上
中文乱码问题:CSV 要加 BOM,Excel 不用
Windows 版 Excel 默认用 GBK 解析 CSV,而 Go 输出的是 UTF-8,不加标识就会显示为方块或问号。这不是 Go 编码错了,是 Excel 没“认出”编码。
解决方案唯一可靠的是写入 UTF-8 BOM:\xEF\xBB\xBF,放在 CSV 内容最开头:
ctx.ResponseWriter.Write([]byte("\xEF\xBB\xBF"))
writer := csv.NewWriter(ctx.ResponseWriter)
writer.Write([]string{"姓名", "城市"})
// ... 其他 Write
writer.Flush()
Excel(.xlsx)文件本身是 ZIP 包含 XML,天然支持 UTF-8,只要用 excelize 写入,中文完全正常,加 BOM 反而出错。
Beego Controller 里导出大文件容易卡死?得控制内存和流式写入
直接查百万行数据再塞进内存生成文件,会 OOM。Beego 没内置流式响应封装,需手动配合 context.Context 和分页查询。
关键动作:
- 数据库查数据用
LIMIT/OFFSET或游标分页,每次取 1000–5000 行 - 对 CSV:复用同一个
csv.Writer,每写完一批调writer.Flush(),避免缓冲区撑爆 - 对 Excel:用
excelize.StreamWriter(v2.8+ 支持),它允许边查边写,不缓存整张表到内存 - 务必设超时:
ctx.Input.SetData("ctx", ctx.Ctx.Request.Context()),并在循环中 select 检查ctx.Done()
真正难的不是“怎么写出来”,而是“怎么让百万行不崩服务、不拖垮浏览器、用户还能中途取消”。这些细节没压住,上线后第一个导出请求就可能把整个 API 实例拖挂。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











