写点什么

【Go】Go 操作 excel 代码封装

作者:非晓为骁
  • 2022-10-12
    福建
  • 本文字数:2869 字

    阅读完需:约 9 分钟

github 地址:https://github.com/zxmfke/train/tree/main/excel


平时写后端管理平台的时候,必不可少地写表格的导出导入功能,因此封装一下。github.com/xuri/excelize/v2这个包其实就可以封装 excel,不过每次写入的时候都需要要指定位置,就是要指定写 excel 表格 A 列还是 B 列。这个包的主要目的也就是写入的时候,不需要每次都指定 X 轴和 Y 轴,除非是针对要写入特定位置的。



注意:这个代码的封装主要目的就是能够导出一个 excel 和读一个 excel,不包含颜色呀,样式什么的。

Excel 操作逻辑

  • 写入 excel 的时候,需要指定 X 轴和 Y 轴,X 轴是大写英文字母,Y 轴是以 1 开始递增的数字。X 轴 26 个英文字母用完后,以AAAB的格式扩张;

  • 每次写入的是一个 sheet,不能一次写入多个 sheet,可以切换 sheet 来写入数据;

  • 读取 excel 的时候,返回是一个切片,每个切片的元素的类型是 map,key 为标题的值,value 就是对应的数据。

Example

func main() {   // 初始化 excel operator    operator := excel.New([]excel.SheetConf{        {Name: "测试中文Tab", ColLen: 20},    })    defer operator.Close()
// 写入header headers := []string{}
for i := 0; i < 20; i++ { headers = append(headers, fmt.Sprintf("%d", i)) }
if err := operator.WriteHeader(headers); err != nil { fmt.Println(fmt.Errorf("write header err : %s", err.Error())) return }
// 写入数据 data := make([][]string, 5)
for i := 0; i < 5; i++ { data[i] = make([]string, 20) for j := 0; j < 20; j++ { data[i][j] = fmt.Sprintf("%d%d", i, j) } }
if err := operator.WriteAll(data); err != nil { fmt.Println(fmt.Errorf("write data err : %s", err.Error())) return }
// 保存 excel _ = operator.SaveAs("testdata/new.xlsx")}
复制代码

Export 方法说明

初始化一个 operator -- New
type SheetConf struct {    Name   string `json:"name"`    ColLen int    `json:"colLen"`}
// New 初始化返回一个Excel操作类,默认使用Sheet1作为首页和当前操作的sheetfunc New(sheets []SheetConf) *Operator {
if len(sheets) == 0 { sheets = defaultSheets }
var ( defaultSheet = sheets[0] operator = &Operator{ curSheet: defaultSheet.Name, excelFile: excelize.NewFile(), sheetMap: make(map[string]*sheet), } )
defer func() { // excelize.NewFile会默认创建一个Sheet1的sheet if len(sheets) != 0 { operator.excelFile.DeleteSheet("Sheet1") } }()
for i := 0; i < len(sheets); i++ { sheetIndex := operator.newSheet(sheets[i].Name) operator.sheetMap[sheets[i].Name] = &sheet{ index: sheetIndex, name: sheets[i].Name, colMaxLen: sheets[i].ColLen, rowCellTemplate: nil, } }
operator.SwitchSheet(defaultSheet.Name)
return operator}
复制代码


函数New用来初始化一个 excel 的operator,对 excel 的写入都是通过operator提供的方法。


SheetConf类型,里面的Name就是 excel 表格每个 sheet 的名称,ColLen是这个 sheet 的顶部 header 的数量



如果传入的是一个空的 SheetsConf,会初始化一个Sheet1的 sheet,不会报错


初始化之后,默认切到第一个 sheet,作为要接下去要写入的 sheet

切换 sheet -- SwitchSheet
// SwitchSheet 切换excel的tabfunc (o *Operator) SwitchSheet(sheetName string) {
var ( sheetTab = o.sheetMap[sheetName] )
sheetTab.initHeader() o.excelFile.SetActiveSheet(sheetTab.index) o.curSheet = sheetName}
复制代码


SwitchSheet会去初始化 Sheet 的 header 定位,每个 sheet 都有自己的rowCellTemplate,即每次要 write 的时候的定位模板。原本需要 write excel 时指定 X 和 Y,在接下去的 write 的方法就只需要传入写入的值即可。


type sheet struct {    index           int    name            string    colMaxLen       int    rowCellTemplate []*cell}
type cell struct { Col string // 横轴字母表 Row int // 第几行 Value string}
复制代码


比如colMaxLen是 5,那rowCellTemplate切片也会初始化 5 个值:


[{Col: "A"},{Col: "B"},{Col: "C"},{Col: "D"},{Col: "E"}]
复制代码
写入 excel -- Write

WriteHeader


此函数默认写入第一行,当做 header 来写入,默认写入数据前,都要先写入一下 header


// WriteHeader 写入headerfunc (o *Operator) WriteHeader(headers []string) error {
...
for i := 0; i < len(headers); i++ { curSheet.rowCellTemplate[i].Row = 1 curSheet.rowCellTemplate[i].Value = headers[i] }
return o.WriteRow(curSheet.rowCellTemplate...)}
复制代码


WriteAll


WriteAll是写入所有数据,excel 表格可以理解为二维数组,X 轴就是 A、B、C 这种的,Y 轴是数字递增。传入的数据也必须是二维数组的。


// WriteAll 写入所有的数据,不包括headerfunc (o *Operator) WriteAll(cells [][]string) error {       var rowBias = 2   ...
for i := 0; i < rows; i++ { ...
for j := 0; j < cols; j++ { curSheet.rowCellTemplate[j].Row = i + rowBias curSheet.rowCellTemplate[j].Value = cells[i][j] }
if err := o.WriteRow(curSheet.rowCellTemplate...); err != nil { return err } }
return nil}
复制代码


WriteRow


WriteRow是写入每行数据,WriteHeaderWriteAll也是调用此方法去写入。调用excelFile.SetCellValuelocation,就是cell类型的方法,把 X 轴和 Y 轴拼接起来。


// WriteRow 写入单行数据func (o *Operator) WriteRow(cells ...*cell) error {   curSheet := o.getCurSheet()
if len(cells) > curSheet.colMaxLen { return OutOfColumn }
for _, c := range cells { if err := o.excelFile.SetCellValue(o.curSheet, c.location(), c.Value); err != nil { return err } } return nil}
复制代码
保存 excel -- SaveAs

调用 write 相关方法后,还不能算保存到 excel 里面,需要调用SaveAs的方法来落盘。传入的路径,如果文件夹没有创建的,会自动创建文件夹。

读取 excel - Read

读取 excel 表格,就是用Read方法,返回的是[]map[string]string类型的结果。每个切片的值代表着一行,map 的 key 就是 header 的值,value 就是数据。



单元测试

主要函数的单元测试均已覆盖,可以看包内_test后缀的文件


通过go test -v -coverprofile=cover.out指令可以执行单元测试,单元测试覆盖率在 91.9%。




欢迎指教~


用户头像

非晓为骁

关注

no pain no gain 2019-04-10 加入

用我的勤奋,一点一点地努力,提升自己的能力,拓展自己的视野,提高自己的认知。 我的知乎:https://www.zhihu.com/people/zhengfke

评论

发布
暂无评论
【Go】Go 操作 excel 代码封装_Excel_非晓为骁_InfoQ写作社区