excelize.file 不支持并发写入,须每个 goroutine 独占实例并禁用全局样式缓存;大数据导出需启用流式写入、游标查询、定时 flush;中文、时间、列宽问题源于未设样式与单元格类型;http 导出须正确设置 content-type、content-disposition 及直接写响应体。

并发导出时 panic: "concurrent map read and map write"
这不是你代码写错了,是 excelize.File 实例本身不支持并发写入。哪怕每个 goroutine 都调用 excelize.NewFile(),只要共用默认样式池或未隔离内部状态,就会触发 map 并发读写 panic。
- 绝对不要复用同一个
f *excelize.File实例处理多个导出请求 - 禁用全局样式缓存:在
f := excelize.NewFile()后立即执行f.Styles = nil(v2.7+ 可设excelize.DefaultStyle = nil) - 每个导出任务必须独占一个
f,且不能跨 goroutine 传递该实例 - 若需复用模板(如固定表头/样式),用
f.CloneSheet()或提前生成 base file 再深拷贝字节流,而非共享对象
大数据量(>10 万行)内存持续上涨甚至 OOM
根本问题在于默认模式会把整张 Sheet 的 XML 结构缓存在内存中,逐行 SetCellValue 相当于不断 append DOM 节点——不是慢,是模型错。
- 必须启用流式写入:
sw, _ := f.NewStreamWriter("Sheet1"),它只维护当前行状态,内存稳定在 MB 级 - 数据库查询必须游标遍历:
for rows.Next() { rows.Scan(&id, &name, &ts) },严禁rows.Columns()+ 全量Scan收集到 slice - 每写 5000 行调一次
sw.Flush(),避免内部缓冲区无限膨胀;写完务必sw.Close() - 导出前加
f.SetCalculation(&excelize.Calculation{CalcMode: "manual"}),禁用 Excel 自动重算(即使没公式,excelize 默认也开)
中文乱码、时间显示为 45201.625、列宽太窄文字被截断
这三个现象本质都是「只写值,不设元信息」:Excel 不靠内容猜格式,靠显式样式和单元格类型控制渲染。
- 中文显示方块:创建 sheet 后立刻设字体样式,例如
style, _ := f.NewStyle(&excelize.Style{Font: &excelize.Font{Family: "SimSun", Size: 11}}),再用f.SetCellStyle("Sheet1", "A1", "Z100000", style)批量绑定——别只设表头 - 时间变数字:不能直接
f.SetCellValue("Sheet1", "C1", t),要先转序列值excelize.TimeToExcelTime(t, false),再配样式NumFmt: 22(yyyy-mm-dd hh:mm:ss) - 列宽不够:
f.SetColWidth("Sheet1", "A", "Z", 14),单位是字符宽度,不是像素;中文建议 ≥12,否则自动缩列导致文字被裁
HTTP 响应损坏、下载文件打不开或提示“已损坏”
常见于流式导出未正确衔接 HTTP body 和 Excel 文件生命周期。
-
Content-Type必须是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,不是application/octet-stream -
Content-Disposition中的 filename 必须 URL 编码:filename*=UTF-8''%E6%8A%A5%E8%A1%A8.xlsx,否则中文名在 Chrome/Firefox 下失效 - 务必用
f.Write(w io.Writer)直接写入http.ResponseWriter,不要先SaveAs到临时文件再读取——多一次 IO 且易权限/清理问题 - 写完后不调
http.Flush()或未关闭sw,会导致部分 ZIP 数据未落盘,Excel 打开报结构错误
f + 字体/格式/列宽三要素同步设置,才是高并发大数据导出真正落地的关键。漏掉任意一环,线上都可能表现为偶发性文件损坏或内存缓慢爬升。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











