用 encoding/csv 写 CSV 文件最稳妥:必须用 csv.NewWriter、每行调 w.Write()、写完必 w.Flush();HTTP 响应需设 Content-Type 和 Content-Disposition(含正确引号);流式处理大数据,空值转空字符串,布尔和时间需显式格式化。

用 encoding/csv 写 CSV 文件最稳妥
Go 标准库的 encoding/csv 是唯一推荐方式,它自动处理字段中的逗号、换行、双引号转义,避免手拼字符串导致解析失败。别用 fmt.Fprintf 或字符串拼接——CSV 看似简单,但字段含 \n 或 " 时会直接破坏格式。
实操建议:
- 始终用
csv.NewWriter包裹*os.File或http.ResponseWriter,别直接写[]byte - 每行必须调用
w.Write(),不能只写[]string切片而不触发实际写入 - 写完务必调用
w.Flush(),否则缓冲区内容可能丢失(尤其小文件易踩坑) - 中文字段无需额外编码,Go 字符串默认 UTF-8,但 HTTP 响应头要显式设
Content-Type: text/csv; charset=utf-8
HTTP 下载时设置正确的响应头和文件名
浏览器是否弹出下载框、保存的文件名、编码识别,全靠响应头。漏掉任一关键项,用户可能看到乱码或内容直接在页面渲染。
常见错误现象:CSV 打开后中文是方块、文件名变成 download.csv、点击链接页面刷新而非下载。
实操建议:
w.Header().Set("Content-Type", "text/csv; charset=utf-8")-
w.Header().Set("Content-Disposition", `attachment; filename="data.csv"`)—— 注意双引号需用反引号或转义,且filename值里不能含路径或特殊字符 - 如需动态文件名(如带时间戳),用
filename*=UTF-8''data-20240512.csv格式支持 Unicode,但旧版 IE 不兼容,保守起见仍用 ASCII 文件名
内存敏感场景下避免一次性加载全部数据
导出百万级记录时,若先构造 [][]string 再遍历写入,会触发大量内存分配甚至 OOM。CSV 导出本质是流式操作,应边查边写。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
使用场景:从数据库查游标、读大文件、实时聚合结果。
实操建议:
- 用
rows, err := db.Query(...)配合for rows.Next(),每轮rows.Scan()后立即csvWriter.Write() - 不要把整张表
db.Select(...).Find(&list)到内存再循环写 - 若必须预处理(如加序号、计算列),用
chan []string+ goroutine 拆流,但需控制 buffer size 防止 channel 堵塞 -
csv.Writer自带缓冲,默认 4KB,一般够用;大数据量可csv.NewWriterSize(f, 64*1024)提升吞吐
处理空值、布尔值和时间类型容易出错
csv.Write() 只接受 []interface{} 或 []string,但传入 nil、bool、time.Time 会 panic 或输出意外字符串(如 {0 0})。
参数差异:数据库驱动返回的 sql.NullString、pgtype.Text 等类型需显式判断和转换。
实操建议:
- 空值统一转为空字符串
"",别传nil(csv.Write()不接受nil) - 布尔值用
strconv.FormatBool()转成"true"/"false",别依赖fmt.Sprint()(可能输出%!t(bool=true)) - 时间类型必须指定格式:
t.Format("2006-01-02 15:04:05"),别用t.String()(含时区信息且格式不固定) - 字段含结构体或指针?先解引用并检查非 nil,再取具体字段,否则 runtime panic
最常被忽略的是 Flush() 和 Content-Disposition 中的引号细节——前者导致文件内容截断,后者导致文件名解析失败。这两个点在线上环境出问题时最难排查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










