Go语言Excelize库实战:5分钟搞定数据导入导出(附完整代码)
在业务系统开发中,数据导入导出几乎是每个项目都会遇到的“标配”功能。无论是用户数据批量导入、报表生成,还是数据迁移,Excel文件都是最常用的数据交换格式。过去,很多开发者会选择Python的pandas或Java的POI来处理Excel,但如果你正在构建高性能的Go服务,现在有了更好的选择。
Excelize这个纯Go编写的库,不仅性能出色,而且功能全面,支持从简单的单元格读写到复杂的图表、样式、数据透视表等高级功能。更重要的是,它的API设计非常直观,学习曲线平缓,即便是Go新手也能在短时间内上手。我在最近的一个微服务项目中,就用它替代了原本的Python脚本,处理速度提升了近10倍,内存占用却只有原来的三分之一。
这篇文章将带你从零开始,通过实际案例掌握Excelize的核心用法,让你在5分钟内就能搞定常见的数据导入导出需求。我会分享一些实际项目中踩过的坑和优化技巧,这些经验能帮你少走不少弯路。
1. 环境准备与基础配置
1.1 安装Excelize库
Excelize支持Go 1.16及以上版本,如果你的Go版本较旧,建议先升级。安装过程非常简单,使用Go Modules管理依赖:
go get github.com/xuri/excelize/v2
如果你还在使用传统的GOPATH模式,可以用这个命令:
go get github.com/xuri/excelize
不过我更推荐使用Go Modules,它能更好地管理版本依赖。在实际项目中,我习惯在go.mod文件中明确指定版本号,避免因版本更新导致的不兼容问题:
module your-project-name
go 1.21
require (
github.com/xuri/excelize/v2 v2.10.0
)
注意:Excelize v2版本与v1版本在API上有较大差异,新项目建议直接使用v2版本。如果你需要从v1迁移到v2,官方提供了详细的迁移指南。
1.2 创建第一个Excel文件
让我们从一个最简单的例子开始。假设你需要生成一个包含员工信息的Excel文件:
package main
import (
"fmt"
"github.com/xuri/excelize/v2"
)
func main() {
// 创建新的工作簿
f := excelize.NewFile()
// 设置默认工作表名称(可选)
index := f.NewSheet("员工信息")
// 写入表头
headers := []string{"员工ID", "姓名", "部门", "入职日期", "薪资"}
for i, header := range headers {
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
f.SetCellValue("员工信息", cell, header)
}
// 写入示例数据
employees := [][]interface{}{
{1001, "张三", "技术部", "2023-01-15", 15000},
{1002, "李四", "市场部", "2023-03-22", 12000},
{1003, "王五", "产品部", "2023-06-10", 18000},
}
for rowIdx, employee := range employees {
for colIdx, value := range employee {
cell, _ := excelize.CoordinatesToCellName(colIdx+1, rowIdx+2)
f.SetCellValue("员工信息", cell, value)
}
}
// 设置活动工作表
f.SetActiveSheet(index)
// 保存文件
if err := f.SaveAs("员工信息表.xlsx"); err != nil {
fmt.Println("保存文件失败:", err)
return
}
fmt.Println("Excel文件生成成功!")
}
这个简单的例子展示了Excelize的基本工作流程:创建文件、写入数据、保存文件。但实际项目中,我们通常需要处理更复杂的情况。
1.3 项目结构建议
对于中大型项目,我建议采用这样的目录结构:
project/
├── cmd/
│ └── main.go
├── internal/
│ ├── excel/
│ │ ├── exporter.go # 导出逻辑
│ │ ├── importer.go # 导入逻辑
│ │ └── utils.go # 工具函数
│ └── models/
│ └── employee.go # 数据模型
├── pkg/
├── go.mod
└── go.sum
这样组织代码的好处是职责分离清晰,便于维护和测试。比如,在exporter.go中专门处理导出逻辑:
package excel
import (
"github.com/xuri/excelize/v2"
)
type Exporter struct {
file *excelize.File
}
func NewExporter() *Exporter {
return &Exporter{
file: excelize.NewFile(),
}
}
func (e *Exporter) ExportEmployees(employees []models.Employee) error {
// 具体的导出逻辑
return nil
}
2. 数据导出:从结构体到Excel
2.1 基础数据导出
在实际业务中,我们通常需要将数据库查询结果或API返回的数据导出为Excel。下面是一个完整的示例,展示如何将结构体切片导出为格式良好的Excel文件:
package main
import (
"fmt"
"time"
"github.com/xuri/excelize/v2"
)
// 定义员工结构体
type Employee struct {
ID int `json:"id"`
Name string `json:"name"`
Department string `json:"department"`
JoinDate time.Time `json:"join_date"`
Salary float64 `json:"salary"`
Email string `json:"email"`
}
// 导出员工数据到Excel
func ExportEmployeesToExcel(employees []Employee, filename string) error {
f := excelize.NewFile()
// 创建工作表
index, err := f.NewSheet("员工列表")
if err != nil {
return fmt.Errorf("创建工作表失败: %v", err)
}
// 定义表头样式
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{
Bold: true,
Color: "#FFFFFF",
Size: 12,
},
Fill: excelize.Fill{
Type: "pattern",
Color: []string{"#4F81BD"},
Pattern: 1,
},
Alignment: &excelize.Alignment{
Horizontal: "center",
Vertical: "center",
},
})
// 定义数据行样式
dataStyle, _ := f.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{
Horizontal: "left",
Vertical: "center",
},
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
},
})
// 定义表头
headers := []struct{
Name string
Width float64
}{
{"员工ID", 10},
{"姓名", 15},
{"部门", 20},
{"入职日期", 15},
{"薪资", 12},
{"邮箱", 25},
}
// 设置列宽和表头
for i, header := range headers {
colName, _ := excelize.ColumnNumberToName(i + 1)
f.SetColWidth("员工列表", colName, colName, header.Width)
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
f.SetCellValue("员工列表", cell, header.Name)
f.SetCellStyle("员工列表", cell, cell, headerStyle)
}
// 写入数据
for rowIdx, emp := range employees {
rowNum := rowIdx + 2 // 表头占第1行
// 员工ID
cell, _ := excelize.CoordinatesToCellName(1, rowNum)
f.SetCellValue("员工列表", cell, emp.ID)
// 姓名
cell, _ = excelize.CoordinatesToCellName(2, rowNum)
f.SetCellValue("员工列表", cell, emp.Name)
// 部门
cell, _ = excelize.CoordinatesToCellName(3, rowNum)
f.SetCellValue("员工列表", cell, emp.Department)
// 入职日期(格式化为字符串)
cell, _ = excelize.CoordinatesToCellName(4, rowNum)
f.SetCellValue("员工列表", cell, emp.JoinDate.Format("2006-01-02"))
// 薪资(设置数字格式)
cell, _ = excelize.CoordinatesToCellName(5, rowNum)
f.SetCellValue("员工列表", cell, emp.Salary)
// 设置货币格式
currencyStyle, _ := f.NewStyle(&excelize.Style{
NumFmt: 4, // 货币格式
})
f.SetCellStyle("员工列表", cell, cell, currencyStyle)
// 邮箱
cell, _ = excelize.CoordinatesToCellName(6, rowNum)
f.SetCellValue("员工列表", cell, emp.Email)
// 设置整行样式
startCell, _ := excelize.CoordinatesToCellName(1, rowNum)
endCell, _ := excelize.CoordinatesToCellName(6, rowNum)
f.SetCellStyle("员工列表", startCell, endCell, dataStyle)
// 隔行变色
if rowIdx%2 == 1 {
altStyle, _ := f.NewStyle(&excelize.Style{
Fill: excelize.Fill{
Type: "pattern",
Color: []string{"#F2F2F2"},
Pattern: 1,
},
})
f.SetCellStyle("员工列表", startCell, endCell, altStyle)
}
}
// 冻结首行(表头)
f.SetPanes("员工列表", &excelize.Panes{
Freeze: true,
XSplit: 0,
YSplit: 1,
TopLeftCell: "A2",
ActivePane: "bottomLeft",
})
// 设置活动工作表
f.SetActiveSheet(index)
// 删除默认的Sheet1
f.DeleteSheet("Sheet1")
// 保存文件
if err := f.SaveAs(filename); err != nil {
return fmt.Errorf("保存文件失败: %v", err)
}
return nil
}
func main() {
// 模拟员工数据
employees := []Employee{
{
ID: 1001,
Name: "张三",
Department: "技术部",
JoinDate: time.Date(2023, 1, 15, 0, 0, 0, 0, time.UTC),
Salary: 15000.00,
Email: "zhangsan@company.com",
},
{
ID: 1002,
Name: "李四",
Department: "市场部",
JoinDate: time.Date(2023, 3, 22, 0, 0, 0, 0, time.UTC),
Salary: 12000.00,
Email: "lisi@company.com",
},
// 更多数据...
}
if err := ExportEmployeesToExcel(employees, "员工信息导出.xlsx"); err != nil {
fmt.Printf("导出失败: %v\n", err)
} else {
fmt.Println("导出成功!")
}
}
这个示例展示了几个实用技巧:
- 样式设置:表头使用蓝色背景,数据行有边框和隔行变色
- 列宽调整:根据内容设置合适的列宽
- 数字格式:薪资列使用货币格式
- 冻结窗格:滚动时表头保持可见
- 日期格式化:将time.Time转换为易读的字符串格式
2.2 高级导出功能
2.2.1 添加图表
Excelize支持多种类型的图表,下面是如何在导出的Excel中添加柱状图的示例:
func AddChartToSheet(f *excelize.File, sheetName string) error {
// 准备图表数据
categories := map[string]string{
"A2": "第一季度", "A3": "第二季度", "A4": "第三季度", "A5": "第四季度",
}
values := map[string]int{
"B2": 45000, "B3": 52000, "B4": 48000, "B5": 61000,
}
// 写入数据
for cell, value := range categories {
f.SetCellValue(sheetName, cell, value)
}
for cell, value := range values {
f.SetCellValue(sheetName, cell, value)
}
// 创建图表
if err := f.AddChart(sheetName, "D2", &excelize.Chart{
Type: excelize.Col3DClustered,
Series: []excelize.ChartSeries{
{
Name: fmt.Sprintf("%s!$A$2", sheetName),
Categories: fmt.Sprintf("%s!$A$2:$A$5", sheetName),
Values: fmt.Sprintf("%s!$B$2:$B$5", sheetName),
},
},
Title: []excelize.RichTextRun{
{
Text: "季度销售额统计",
Font: &excelize.Font{
Bold: true,
Size: 14,
},
},
},
Legend: excelize.ChartLegend{
Position: "bottom",
},
PlotArea: excelize.ChartPlotArea{
ShowCatName: true,
ShowLeaderLines: false,
ShowPercent: true,
ShowSerName: true,
ShowVal: true,
},
}); err != nil {
return fmt.Errorf("添加图表失败: %v", err)
}
return nil
}
2.2.2 数据验证
在某些场景下,我们需要限制用户在Excel中输入的数据类型或范围:
func AddDataValidation(f *excelize.File, sheetName string) error {
// 添加下拉列表
dvRange := excelize.NewDataValidation(true)
dvRange.Sqref = "C2:C100"
dvRange.SetDropList([]string{"技术部", "市场部", "产品部", "人事部", "财务部"})
dvRange.SetInput("请从下拉列表中选择部门", "请选择部门")
dvRange.SetError("错误", "请从列表中选择有效的部门", "stop")
if err := f.AddDataValidation(sheetName, dvRange); err != nil {
return fmt.Errorf("添加数据验证失败: %v", err)
}
// 添加数字范围验证
numRange := excelize.NewDataValidation(true)
numRange.Sqref = "E2:E100"
numRange.SetRange(0, 1000000, excelize.DataValidationTypeWhole, excelize.DataValidationOperatorBetween)
numRange.SetInput("请输入0-1000000之间的数字", "薪资范围")
numRange.SetError("错误", "薪资必须在0到1000000之间", "stop")
if err := f.AddDataValidation(sheetName, numRange); err != nil {
return fmt.Errorf("添加数字验证失败: %v", err)
}
return nil
}
2.2.3 条件格式
条件格式可以让数据更加直观:
func AddConditionalFormatting(f *excelize.File, sheetName string) error {
// 高亮薪资超过50000的单元格
format1, err := f.NewConditionalStyle(&excelize.Style{
Fill: excelize.Fill{
Type: "pattern",
Color: []string{"#FFC7CE"},
Pattern: 1,
},
Font: &excelize.Font{
Color: "#9C0006",
},
})
if err != nil {
return err
}
// 应用条件格式
if err := f.SetConditionalFormat(sheetName, "E2:E100", []excelize.ConditionalFormatOptions{
{
Type: "cell",
Criteria: "greaterThan",
Format: format1,
Value: "50000",
}

&spm=1001.2101.3001.5002&articleId=152440299&d=1&t=3&u=45c09b455d6746d38a46033de7509e3e)
665

被折叠的 条评论
为什么被折叠?



