excelize 操作 .xlsx 文件需严格遵循 ooxml 规范,文件不标准、utf-8 编码错误、未设单元格格式或滥用 api 均会导致乱码、崩溃等问题,应优先验证文件结构与字符串合法性。

excelize 是当前 Go 生态中操作 .xlsx 文件最可靠的选择,但它的行为高度依赖文件格式合规性、写入方式和类型处理逻辑——用错一个函数或参数,轻则中文乱码、公式不生效,重则文件损坏、OOM 崩溃。
打开 .xlsx 文件报 "unsupported file format"
这不是代码问题,而是文件本身不标准。excelize 只认严格符合 OOXML 规范的 .xlsx,不接受:
- WPS「兼容模式」保存的文件
- 手动把
.xls改后缀成.xlsx -
.xlsm(即使禁用宏也不支持) - LibreOffice 默认导出的某些变体
验证方法:
- Linux/macOS 下执行
file your.xlsx:输出应含Zip archive data,开头是PK\x03\x04 - 执行
unzip -l your.xlsx:必须包含[Content_Types].xml和xl/workbook.xml
修复建议:
- 用 Excel for Mac/Windows 或 LibreOffice「另存为 → Excel 工作簿(*.xlsx)」
- 别选「Excel 97–2003」或「兼容模式」
- 若需处理
.xls或加密文件,先用外部工具转格式,excelize不支持
写入中文显示 ??? 或方块,SetCellValue 没效果
根本原因不是字体,而是编码或单元格格式没设对。Excel 本身不存“字体”,只存原始字节 + 格式代码。
- 确保输入字符串是合法 UTF-8:
utf8.ValidString(s)返回true再传入 - 避免从 GBK 编码的旧模板或 CSV 直接读取后强转
string([]byte{}) - 单元格需显式启用自动换行:
f.SetCellStyle("Sheet1", "A1", "A1", styleID),其中styleID来自f.NewStyle(&excelize.Style{Alignment: &excelize.Alignment{WrapText: true}}) - 列宽不足也会导致中文截断:用
f.SetColWidth("Sheet1", "A", "A", 25)配合WrapText
导出 5 万行以上内存暴涨、速度慢
默认每调一次 SetCellValue 都会解析坐标、更新内部 XML 节点树,10 万 × 10 列就可能吃掉 1GB+ 内存。
- 批量写整行:用
f.SetSheetRow("Sheet1", "A1", &[]interface{}{"ID", "Name"}),比循环快 5–8 倍 - 超 3 万行纯数据导出:改用
StreamWriter:sw, _ := f.NewStreamWriter("Sheet1")→sw.SetRow("A1", rowSlice)→sw.Flush() - 导出到 HTTP 接口时,别用
SaveAs(),改用f.WriteTo(w io.Writer)直接流式输出 - 注意:
SetSheetRow第三个参数必须是&slice,漏掉&会导致静默失败
GetRows() 返回空或 panic
GetRows() 会把整张表加载进内存,且对空行、隐藏行、合并单元格无区分,极易错位或 OOM。
- 安全读法是按需拉取:
f.GetCellValue("Sheet1", fmt.Sprintf("A%d", i)),自己控制行号,遇空值即停 - 先确认 sheet 名是否正确:
f.GetSheetMap()打印出来看,常见错误是代码写Sheet1,实际文件里是工作表1或带空格 - 若 Sheet 被 VBA 隐藏(
state="veryHidden"),默认会被跳过;可加选项excelize.Options{SkipUnsupportedSheets: false}强制加载,但结构损坏时可能 panic -
GetCellValue对公式/日期返回的是渲染结果,易误判;优先用f.GetCellType()判断类型,日期值用f.GetCellFloat()拿原始浮点数再转time.DateFromExcel
utf8.ValidString,很容易绕半天才意识到问题根本不在 Go 代码里。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











