导出五万行对账单,最初用 SetCellValue 一格一格写,内存冲到 1.2G,进程被系统直接干掉。换成流式写入,同样的数据不到 100M。
先把库选对
go get github.com/xuri/excelize/v2 拉下来,这是目前维护最活跃的 Go 表格库,读写都覆盖。早年那套 360 开头的命名空间版本已经停更,新项目别引。
万行以内直接写单元格
f := excelize.NewFile(); f.SetCellValue(`Sheet1`, `A1`, `订单号`); f.SaveAs(`out.xlsx`)写法直观,样式和合并单元格都好加,数据量小的时候没必要上复杂方案。Go 的字符串可以用反引号包,省掉一层转义,写中文表头也清爽。
大数据换 StreamWriter
sw, _ := f.NewStreamWriter(`Sheet1`); for row := 1; row <= 50000; row++ { cell, _ := excelize.CoordinatesToCellName(1, row); sw.SetRow(cell, []interface{}{row, `测试`, 12.5}) }; sw.Flush()流式写入不把整个工作簿留在内存里,写一行刷一行。代价是写出去的行不能回头改,样式必须在写之前设好。SetRow 的第一个参数是起始单元格,不是行号,这里最容易写错。
读取也别一次全读进来
GetRows 会把整张表读成二维切片,几十兆的文件直接吃满内存。大文件用 Rows 迭代逐行取,用完关闭,列多的时候只读需要的列。
还有几个问题,几乎每次导出都会碰上。
- 数字列被识别成文本,单元格左上角出现绿色三角,用设置单元格格式补齐。
- 时间字段直接写 time.Time 会带时区,导出来差几个小时,统一格式化后再写。
- 文件被占用时保存失败,返回的错误要接住,别用下划线吞掉。
选型没什么纠结:要样式和图表就用常规接口,纯导数据走流式,十万行以上考虑拆文件或者直接给 CSV,浏览器打开还更快。