使用 Golang 和 Excelize 高效创建与读取 Excel 文件
本文提出一种使用 Golang 进行 Excel 文件创建和读取的方案。首先对问题进行分析,引出方案的基本架构;然后分章节描述了 Excelize 基础库的基本用法,以及 Excel 数据在 Golang 中的表示和解析方式,并进一步提出了应对大规模数据写入场景的优化方法;最后,指出了一些可能遇到的问题和对策。
引言
在企业管理与后端研发中,经常需要处理 Excel 表格。例如系统管理员需要定期汇总参与人信息、股权激励的授予或归属情况,或者通过修改 Excel 数据表进行批量更新。随着 Golang 在后端领域的广泛应用,如何实现灵活通用且兼顾大规模数据导出效率的 Excel 解决方案,成为一个值得探讨的问题。
核心架构设计
要实现一个可靠的 Excel 数据处理方案,主要涉及三个关键环节:管理文件状态的数据对象、将结构体字段映射为 Excel 列的数据表示,以及连接两者的反射解析逻辑。
文件对象管理
引入一个集中的 Excel 文件对象作为切入点,封装工作簿的所有信息(包括工作表、行列数据、单元格格式等)。所有的增删改查操作都基于该对象进行。
数据表示与解析思路
将 Excel 工作表的每一列映射为 Golang 的一个结构体字段。除了保存字段的实际值外,还需通过某种格式记录列名、数据验证规则等信息。通过 Golang 的反射机制解析这些附加信息,指导程序如何从内存结构体正确写入文件,或反向还原。
整体架构流程
最终落地的架构通常包含以下组件:
- 文件操作层: 使用成熟的底层库(如 Excelize)处理文件的打开、保存及单元格级操作。
- 数据定义层: 使用带有自定义 Tag 的结构体来声明表格结构。
- 解析引擎: 利用 Golang 反射机制解析 Tag 元数据,桥接代码与 Excel 文件的交互。
准备工作
在实际编码前,需引入 github.com/xuri/excelize/v2 依赖包。该库支持 Golang 1.15 及以上版本,提供了丰富的 API 来处理 Office Excel 文档。
基础操作详解
第1步:初始化与加载文件
目的: 获取可操作的 Excel 对象实例。动作: 调用 excelize.NewFile() 创建空白文件;若需读取现有文件,可使用 OpenReader 或 OpenFile 函数。结果: 得到一个包含工作簿信息的 *excelize.File 对象。
第2步:确定单元格坐标
目的: 准确定位需要设置样式或填充数据的区域。入口: CoordinatesToCellName(row, col)。动作: 传入行号和列号生成坐标字符串(如 “A1”)。指定左上角和右下角坐标可定义矩形区域。结果: 获得可用于其他 API 调用的精确坐标。
第3步:应用单元格样式
目的: 统一设置字体大小、颜色及对齐方式。参数: 颜色值、粗细设置。动作: 先通过 NewStyle(style) 创建样式并获得整数索引,再通过 SetCellStyle(coord, coord, styleID) 应用到目标区域。
第4步:基本表格元素设置
目的: 完善表格外观。动作: 使用 SetRowHeight 和 SetColWidth 调整行列尺寸;使用 MergeCell(startCoord, endCoord) 合并单元格;使用 SetCellValue(coord, value) 填入各类类型数据(字符串、数字、时间、布尔值等)。
第5步:配置下拉列表(数据验证)
目的: 规范用户输入。步骤:
- 创建验证对象:
vd := excelize.NewDataValidation()。 - 设定生效范围:设置
Sqref属性(如 “A1:A10”)。 - 添加选项:调用
SetDropList(vd, options)指定下拉内容。 - 写入对象:调用文件对象的
AddDataValidation(vd)生效。
基于反射的通用读写实现
第一步:通过结构体 Tag 表示列属性
为了自动化处理不同业务场景,我们定义一个统一的 Tag 键(如 ex),并在其值中描述列特征。示例结构体如下:
type PeopleInfo struct {
PeopleNo string `ex:"head:工号;type:string;required;color:#0070C0"`
PeopleName string `ex:"head:姓名;type:string;required"`
BirthDate string `ex:"head:出生日期;type:date;omitempty"`
EmploymentStatus string `ex:"head:在职状态;type:string;select:在职,离职"`
}
head:指定对应的 Excel 列标题。type:解析时将字段值视为此数据类型。select:触发下拉列表功能,后跟逗号分隔的枚举值。required:必须非零值,否则写入报错。omitempty:值为零值时留空单元格。color:列名单元格的背景色。
第二步:解析 Tag 元数据
编写辅助函数,利用 Golang 的 reflect 包提取结构体的 Tag 值,并组装成通用的配置结构体(例如名为 Setting 的类型),以存储解析后的 Head, Type, Required 等布尔或切片字段。
第三步:执行写入操作
入口: 迭代传入的结构体实例列表。动作:
- 遍历每个结构体的 Field。
- 根据解析出的
OmitEmpty判断是否跳过。 - 依据
Type转换字段值。 - 计算当前行列坐标(行号通常从 2 开始,因首行为列名)。
- 调用
SetCellValue(“Sheet1”, axis, val)写入。
第四步:执行读取操作
入口: 已打开的 File 对象及用于存放结果的切片指针。动作:
- 调用
GetRows(“Sheet1”)获取所有数据行。 - 按索引遍历每一列的文本内容。
- 创建新的结构体实例,并使用反射将对应的单元格字符串 Set 给字段。
- 将组装好的实例追加至接收切片中。
高性能场景优化
问题现象
上述逐单元格(Cell-level)写入方式在处理十万级以上大数据量时,由于频繁的文件刷新与内存分配,会导致耗时长且性能不佳。
解决方案:流式写入 (Stream Writer)
Excelize 提供了流式 API,允许按行级别写入数据,大幅提升 I/O 效率。
操作流程
- 创建流式写入器: 调用
ef.NewStreamWriter(“Sheet1”)。 - 构造行数据: 对于每一行,构造
[]interface{}切片。若需带样式,可将元素定义为excelize.Cell结构体,包含Value和StyleID。 - 写入行: 调用流式对象的
SetRow(axis, rowSlice, excelize.RowOpts{Height: float64})。 - 结束写入: 必须显式调用
sw.Flush(),否则后续保存文件时会丢失未刷盘的数据。 - 注意: 设置列宽(Column Width)需在开启流式模式之前完成;不可将流式与非流式 API 混用。
避坑指南
大量枚举值的下拉设置
SetDropList 对拼接成的选项字符串有严格的字符数限制(不能超过 255 字符)。当枚举项过多超出限制时,应:
- 新建一个仅用于存放枚举的工作表(如
enum)。 - 将该表某一列(如
A列第 2 行至第 10 行)填入枚举值。 - 使用
SetSqrefDropList指向该区域引用(如 “enum!A2:A10”)。
大工作表读取异常
现象: 打开超大文件后另存为,新文件损坏变小,无法打开。
原因: Excelize 默认限制单个 Sheet 解压内存上限(16 MB),超过部分写入 OS 临时文件;而 SaveAs 操作忽略了这些临时路径数据。
对策: 增大配置参数 WorksheetUnzipMemLimit(上限可达 UnzipSizeLimit 的 16 GB 限制),避免触发生效条件。
结语
本方案通过结构体 Tag 配合反射机制,实现了 Excel 业务模型与文件结构的双向自动化映射。结合 Excelize 的标准库与流式 API,不仅满足了日常小数据量的灵活读写需求,也解决了大规模数据导出的性能瓶颈,同时针对长下拉框和大文件读取等特定场景提供了切实可行的修复策略。
以上就是如何使用Golang创建与读取Excel文件以及解决常见问题的详细内容,更多关于Golang读写Excel优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









