中文乱码或方块字的根本原因是字体与编码未同步处理:go字符串虽为utf-8,但excelize默认使用calibri等西文字体,需显式设置simsun或microsoft yahei字体并批量绑定样式;大数据量卡死源于内存全载入,须用streamwriter流式写入+分批flush;时间字段显示为数字需配合timetoexceltime转换与numfmt=22格式设置;http响应损坏多因content-type、content-disposition未精确配置或流未正确关闭。

中文乱码或方块字:字体和编码必须同步处理
导出后 Excel 显示“□□□”或“???”,不是数据本身有问题,而是 Excel 渲染时找不到能显示中文的字体。Go 字符串天然是 UTF-8,但 excelize 默认用 Calibri 这类西文字体,Windows/macOS 打开时 fallback 失败就出方块。
- 创建文件后立即调用
f.SetFontFamily("Sheet1", "SimSun")或f.SetFontFamily("Sheet1", "Microsoft YaHei"),确保新写入单元格用中文字体 - 更稳妥的做法是定义完整样式:
style, _ := f.NewStyle(&excelize.Style{Font: &excelize.Font{Family: "SimSun", Size: 11}}),再用f.SetCellStyle("Sheet1", "A1", "Z1000", style)批量绑定 - 不要只设表头字体而漏掉数据行;也不要依赖
f.SetCellValue自动推断——它不带样式 - 若用
f.NewStreamWriter,需在sw.SetRow前手动调用f.SetCellStyle应用到目标区域,流式写入不自动继承样式
大数据量卡死或 OOM:必须用流式 + 分页 + 手动 flush
查 50 万行进内存再逐行 f.SetCellValue,Go 进程 RSS 很快破 2GB,Excel 写入也阻塞主线程。这不是性能问题,是资源模型错误。
- 数据库查询必须分页或游标遍历:
for rows.Next() { rows.Scan(&id, &name, &ts) },别用rows.Columns()+ 全量Scan收集 - 启用流式写入:
sw, _ := f.NewStreamWriter("Sheet1"),然后sw.SetRow("A1", headers)和循环sw.SetRow(fmt.Sprintf("A%d", i+2), []interface{}{id, name, ts}) - 每写 5000 行调一次
sw.Flush(),避免内部缓冲无限增长;写完务必sw.Close() - 导出前加
f.SetCalculation(&excelize.Calculation{CalcMode: "manual"}),禁用公式重算(即使没公式,excelize 也会默认开启)
时间字段变成数字 45201.625:必须显式转序列值 + 设 NumFmt
f.SetCellValue("Sheet1", "C2", u.At) 看似简单,实际只写入 float64 类型的 Excel 序列号,单元格格式仍是“常规”,Excel 打开就显示为小数。
- 正确写法:
excelize.TimeToExcelTime(u.At, false)(第二个参数false表示 1900 日期系统,Windows/Mac 兼容) - 配套设置数字格式:
dateStyle, _ := f.NewStyle(&excelize.Style{NumFmt: 22})(22 =yyyy-mm-dd h:mm:ss),再f.SetCellStyle("Sheet1", "C2", "C10000", dateStyle) - 不要用
f.SetCellDateTime——它内部调用TimeToExcelTime但不设格式,仍需额外SetCellStyle - 若用
sw.SetRow,日期字段必须提前转好:[]interface{}{id, name, excelize.TimeToExcelTime(u.At, false)},再统一设列样式
HTTP 响应文件损坏或无法下载:Content-Type 和 Content-Disposition 要精确
浏览器提示“Excel 发现不可读内容”或直接打开空白页,大概率是响应头没配对,或者 f.Write 没走完就返回。
- 响应头必须三件套:
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")(不能用DetectContentType,它对 xlsx 返回application/zip) -
w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape("用户列表.xlsx")),注意filename*=+url.PathEscape,否则中文名在 Chrome/Firefox 下乱码 - 别先
f.WriteToBuffer()再w.Write(buf.Bytes())——大文件会吃光内存;直接f.Write(w)流式输出到 ResponseWriter - 务必在
f.Write(w)后检查 error,网络中断或客户端取消时会返回io.ErrClosedPipe,需记录日志但不必 panic
sw.Flush() 漏掉,或一个 url.PathEscape 忘加,就导致线上导出失败。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











