导出csv时必须正确设置http头,否则浏览器会当作html渲染;gin中需用c.header()显式设置content-type为"text/csv; charset=utf-8"和content-disposition为"attachment; filename=xxx.csv",并优先写入utf-8 bom(\ufeff)防止中文乱码,使用csv.writer流式写入响应体避免内存溢出。

导出 CSV 时 HTTP 头必须设对,否则浏览器当网页打开
浏览器默认把响应当 HTML 渲染,不加正确头就看不到下载弹窗或内容错乱。Gin 中要用 c.Header() 显式设置 Content-Type 和 Content-Disposition:
c.Header("Content-Type", "text/csv; charset=utf-8")c.Header("Content-Disposition", "attachment; filename=data.csv")
注意 filename 不能含中文(部分浏览器解析失败),建议用英文+时间戳,比如 export_20240515.csv;若真要支持中文,得 URL 编码,但兼容性差,不如避开。
用 csv.Writer 写入响应体,别拼字符串
手动拼接 CSV 容易出引号、逗号、换行符转义问题(比如字段含换行或双引号)。Go 标准库 csv.Writer 自动处理这些,且写入 c.Writer(即 HTTP 响应流)能避免内存堆积:
writer := csv.NewWriter(c.Writer)
defer writer.Flush()
err := writer.Write([]string{"id", "name", "email"})
if err != nil {
c.AbortWithStatusJSON(500, gin.H{"error": "write header failed"})
return
}
关键点:writer.Flush() 必须调用,否则缓冲区数据不写出;c.Writer 是 http.ResponseWriter 的封装,直接写它,不经过 Gin 的 JSON/HTML 渲染流程。
大数据量导出要流式写入,别全 load 到内存
查数据库后全塞进 slice 再遍历写 CSV,10 万行就可能 OOM。应该边查边写:
- 用数据库游标或分页查询(如 PostgreSQL 的
cursor,MySQL 的limit offset) - 每 fetch 一批(比如 1000 行),立刻
writer.WriteAll()写出 - 避免
SELECT *,只选需要字段,减少网络和内存开销
示例伪代码:rows, _ := db.Query("SELECT id,name,email FROM users WHERE ..."),然后 for rows.Next() 循环读取并写入 writer,全程无大 slice。
中文字段导出乱码?UTF-8 BOM 是个妥协方案
Excel 默认用系统编码打开 CSV,Windows 上常显示乱码。标准解法是加 UTF-8 BOM(\uFEFF)头:
c.Writer.Write([]byte("\uFEFF"))
writer := csv.NewWriter(c.Writer)
这不是标准 CSV 规范,但 Excel 识别它为 UTF-8;其他工具(如 VS Code、Numbers)通常也兼容。别用 charset=gbk,HTTP 协议里 Content-Type 不支持 GBK 声明,且现代系统基本只认 UTF-8。
真正麻烦的是字段里有 Excel 特殊字符(如等号开头触发公式执行),这种得前端加单引号前缀或服务端转义,但属于业务层防御,不是 Gin 或 CSV 库能解决的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











