excelize 是 Go 生态操作 .xlsx 最可靠的选择,但严格依赖文件合规性、写入方式与类型处理:不支持非标准格式(如 WPS 兼容模式、.xls/.xlsm)、中文需 UTF-8 源数据、大批量用 StreamWriter、读公式/日期需判类型并手动计算。

直接说结论:excelize 是当前 Go 生态中操作 .xlsx 文件最可靠的选择,但它的行为高度依赖文件格式合规性、写入方式和类型处理逻辑——用错一个函数或参数,轻则中文乱码、公式不生效,重则文件损坏、OOM 崩溃。
打开 .xlsx 失败报 "unsupported file format"
这不是代码写错了,而是文件本身不标准。Excelize 只认严格符合 OOXML 规范的 .xlsx,不接受 WPS「兼容模式」、手动改后缀的 .xls、带宏的 .xlsm,甚至 LibreOffice 默认保存的某些变体也会被拒。
- 先用
unzip -l your.xlsx检查内部结构:必须含[Content_Types].xml和xl/workbook.xml,缺一不可 - 用
file your.xlsx(Linux/macOS)或 PowerShell 的Get-Item your.xlsx | % { $_.Length }确认是 ZIP 包且非空 - 若确认是 WPS 或旧版 Excel 导出,务必用 Excel for Mac / Windows 或 LibreOffice「另存为 → Excel 工作簿(*.xlsx)」,别选「Excel 97-2003」或「兼容模式」
- 遇到
.xls、.csv、加密文件,得先转格式;.xlsm即使禁用宏也不支持,别硬试
写入中文显示 ??? 或方块,SetCellValue 没效果
Excelize 默认按 UTF-8 解析字符串,但如果你的数据源头混了 BOM、GBK 字节或非法 UTF-16,它不会帮你转换,只会原样塞进 XML——Excel 打开时就懵了。
- 确认源数据是合法 UTF-8:
utf8.ValidString(s)返回true再传给SetCellValue - 从 CSV/数据库读取后别盲目
string([]byte{...}),尤其注意sql.Scan对BLOB字段返回的是[]byte,需显式转string - 若模板文件本身是 GBK 编码(常见于老旧 Windows Excel),excelize 无法修复,只能让上游重存为标准
.xlsx - 中文显示方块?不是字体问题,是单元格没设自动换行+足够列宽:
f.SetColWidth("Sheet1", "A", "A", 25)+f.SetCellStyle(..., &excelize.Style{Alignment: &excelize.Alignment{WrapText: true}})
导出 5 万行以上内存暴涨、速度慢
默认方式把整张表结构全载入内存构建 XML 树,每调一次 SetCellValue 都触发坐标解析和节点重建——10 万行 × 10 列循环调用,性能断崖下跌,内存轻松破 1GB。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 批量写用
SetSheetRow("Sheet1", "A1", &[]interface{}{"ID", "Name"}),比单单元格快 5–8 倍 - 超 3 万行纯数据导出,改用
StreamWriter:sw, _ := f.NewStreamWriter("Sheet1")→sw.SetRow("A1", rowSlice)→sw.Flush(),内存稳定在几 MB -
StreamWriter不支持合并单元格、图表、条件格式,仅适用于日志、订单明细等结构化纯数据 - 避免在循环里反复调
f.SaveAs(),全部写完再存一次;中途检查用f.WriteTo(io.Discard)
读取公式/日期/空单元格总拿不到想要的值
GetCellValue 返回的是 Excel 渲染后的文本,对公式、日期、布尔值做隐式转换,极易误判。它不是“读内容”,而是“读 Excel 当前显示什么”。
- 先用
f.GetCellType("Sheet1", "A1")判断类型:CellTypeFormula、CellTypeDate、CellTypeNumeric—— 别靠== ""判空 - 公式结果为空?不是 bug,是默认不计算:需设
f.Calculation = &excelize.Calculation{CalcMode: "manual"}并调f.Calculate() - 日期存的是浮点数(如
45432),用f.GetCellFloat("Sheet1", "A1")拿原始值,再传给time.DateFromExcel() - 合并单元格只保留左上角值,
GetCellValue("Sheet1", "B1")对 A1:B1 合并后返回空,得先f.GetMergeCells()映射逻辑位置
最容易被忽略的一点:文件是否被 Excel 进程锁住。Windows 下双击打开过一次没关,Go 就读不到完整 ZIP 结构,f.GetSheetMap() 返回空 map 或 panic,连 os.OpenFile(path, os.O_RDONLY, 0) 都会报 permission denied——别急着改代码,先关掉 Excel。


















