文档地址:

https://xuri.me/excelize/zh-hans/

package main

import (
	"fmt"
	"github.com/xuri/excelize/v2"
)

func main() {
	readEecel()
	//writeEecel()
	//appendEecel()
}


func writeEecel() {
	f := excelize.NewFile()
	// 创建一个工作表
	index := f.NewSheet("Sheet1")
	// 设置单元格的值
	f.SetCellValue("Sheet1", "A2", "Hello world.")
	f.SetCellValue("Sheet1", "B2", 100)

	//按行赋值
	err := f.SetSheetRow("Sheet1", "A1", &[]interface{}{"39 - 38 = ", "39 - 38 = ", "39 - 38 = ", "39 - 38 = ", "39 - 38 = "})
	if err != nil {
		fmt.Println(err)
	}

	//设置列宽度
	err = f.SetColWidth("Sheet1", "A", "H", 16)
	if err != nil {
		fmt.Println(err)
	}
	// 设置工作簿的默认工作表
	f.SetActiveSheet(index)
	// 根据指定路径保存文件
	if err = f.SaveAs("Book1.xlsx"); err != nil {
		fmt.Println(err)
	}
}

func appendEecel()  {
	fileName := "Book1.xlsx"
	activeSheet := "Sheet1"
	f, _:= excelize.OpenFile(fileName)
	// Get all the rows in the Sheet1.获取所有行的数据 按行获取
	rows, err := f.GetRows(activeSheet)
	//获取所有列的内容 按列获取
	cols, err := f.GetCols(activeSheet)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("rows_len:", len(rows))
	fmt.Println("cols_len:", len(cols))
	//fmt.Println("rows_content:",rows)

	lineHeight, err := f.GetRowHeight(activeSheet,1)
	fmt.Println("lineHeight:", lineHeight)

	lineLevel, err := f.GetRowOutlineLevel(activeSheet,2)
	fmt.Println("lineLevel:", lineLevel)

	f.SetCellValue(activeSheet, fmt.Sprintf("A%d",len(rows)+1), fmt.Sprintf("aa%d",len(rows)+1))
	f.SetCellValue(activeSheet, fmt.Sprintf("B%d",len(rows)+1), fmt.Sprintf("bb%d",len(rows)+1))
	f.SetCellValue(activeSheet, fmt.Sprintf("C%d",len(rows)+1), fmt.Sprintf("cc%d",len(rows)+1))

	if err := f.SaveAs(fileName); err != nil {
		fmt.Println(err)
	}
}

func readEecel()  {
	//f, err := excelize.OpenFile("Book1.xlsx")
	f, err := excelize.OpenFile("信息系统暴露面模板_20211027.xlsx")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer func() {
		if err := f.Close(); err != nil {
			fmt.Println(err)
		}
	}()
	// 获取工作表中指定单元格的值
	//cell, err := f.GetCellValue("Sheet1", "B2")
	//if err != nil {
	//	fmt.Println(err)
	//	return
	//}
	//fmt.Println(cell)

	// 获取 Sheet1 上所有单元格
	rows, err := f.GetRows("Sheet1")
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, row := range rows {
		for _, colCell := range row {
			fmt.Print(colCell, "\t")
		}
		fmt.Println()
	}
	fmt.Println("===========================================")

	res:= arrayTwoStringGroupsOf(rows,3)
	fmt.Println(res)

}


func arrayTwoStringGroupsOf(arr [][]string, num int64) [][][]string {
	max := int64(len(arr))
	//判断数组大小是否小于等于指定分割大小的值,是则把原数组放入二维数组返回
	if max <= num {
		return [][][]string{arr}
	}
	//获取应该数组分割为多少份
	var quantity int64
	if max%num == 0 {
		quantity = max / num
	} else {
		quantity = (max / num) + 1
	}
	//声明分割好的二维数组
	var segments = make([][][]string, 0)
	//声明分割数组的截止下标
	var start, end, i int64
	for i = 1; i <= quantity; i++ {
		end = i*num
		if i != quantity {
			segments = append(segments, arr[start:end])
		} else {
			segments = append(segments, arr[start:])
		}
		start = i*num
	}
	return segments
}

func arrayInGroupsOf(arr []int, num int64) [][]int {
	max := int64(len(arr))
	//判断数组大小是否小于等于指定分割大小的值,是则把原数组放入二维数组返回
	if max <= num {
		return [][]int{arr}
	}
	//获取应该数组分割为多少份
	var quantity int64
	if max%num == 0 {
		quantity = max / num
	} else {
		quantity = (max / num) + 1
	}
	//声明分割好的二维数组
	var segments = make([][]int, 0)
	//声明分割数组的截止下标
	var start, end, i int64
	for i = 1; i <= quantity; i++ {
		end = i*num
		if i != quantity {
			segments = append(segments, arr[start:end])
		} else {
			segments = append(segments, arr[start:])
		}
		start = i*num
	}
	return segments
}

应用场景:
gorm 从 mysql 里分批查出数据,并分批追加写入excel


// 全量excel分批导出poc
func (e *ModelExploits) ExportModelExploits(c *gin.Context) {

	msgID := tools.GenerateMsgIDFromContext(c)
	d := new(dto.ModelExploitsSearch)
	//fmt.Println("d222", d)
	db, err := tools.GetOrm(c)
	if err != nil {
		log.Error(err)
		return
	}

	//查询列表
	err = d.Bind(c)
	if err != nil {
		e.Error(c, http.StatusUnprocessableEntity, err, "参数验证失败")
		return
	}

	//数据权限检查
	p := actions.GetPermissionFromContext(c)

	serviceStudent := service.ModelExploits{}
	serviceStudent.MsgID = msgID
	serviceStudent.Orm = db
	fileNamePath, err := serviceStudent.ExportModelExploitsPage(d, p)
	if err != nil {
		e.Error(c, http.StatusUnprocessableEntity, err, "查询失败")
		return
	}

	e.OK(c, gin.H{"filePath": fileNamePath}, "ok")
}



func (e *ModelExploits) ExportModelExploitsPage(c *dto.ModelExploitsSearch, p *actions.DataPermission) (fileNamePath string, err error) {
	//var err error
	var data models.ModelExploits
	msgID := e.MsgID

	dirPath := "static/downloads"                                                          //导出的目录
	fileName := fmt.Sprintf("%s_%s", time.Now().Format("20060102150405"), "poc_data.xlsx") //文件名称
	fileNamePath = path.Join(dirPath, fileName)                                            //文件全路径
	batchSize := 100
	var lis []models.ModelExploits
	result := e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			//cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
			actions.Permission(data.TableName(), p),
		).
		//分批处理
		FindInBatches(&lis, batchSize, func(tx *gorm.DB, batch int) error {
			// 批量处理找到的记录
			total := 0
			if len(lis) == batchSize {
				total = batch * batchSize
			} else {
				total = (batch-1)*batchSize + len(lis)
			}
			fmt.Printf("第 %d 批 , 每批 %d 条 ,已经处理 %d 条\n", batch, batchSize, total) //			batch // Batch 1, 2, 3

			//for _, result := range lis {
			//	fmt.Println(result.Id)
			//}

			// 批量处理找到的记录 分批存到excel里
			err := appendSaveEexcel(dirPath, fileName, &lis)
			if err != nil {
				return err
			}
			//tx.Save(&lis)
			//fmt.Println(tx.RowsAffected) // 本次批量操作影响的记录数
			// 如果返回错误会终止后续批量操作
			return nil
		})
	if result.Error != nil {
		log.Errorf("msgID[%s] db error:%s", msgID, result.Error)
		return "", result.Error
	}
	//fmt.Printf("total4444---->:%d", len(lis)) //			batch // Batch 1, 2, 3

	//fmt.Println(result.Error) // returned error
	//fmt.Println(result.RowsAffected) // 整个批量操作影响的记录数
	return fileNamePath, nil
}

//save excel 追加写入
func appendSaveEexcel(dirPath, fileName string, lists *[]models.ModelExploits) error {
	_ = tools.PathDirCreate(dirPath) //不存在创建目录
	activeSheetName := "Sheet1"
	fileNamePath := path.Join(dirPath, fileName)
	exists, err := tools.PathFileExists(fileNamePath) //判断文件是否存在创建目录
	rowNum := 0
	lastLineNum := 0
	var f *excelize.File
	// 创建excel
	if !exists || err != nil {
		f = excelize.NewFile()
		// Create a new sheet.
		index := f.NewSheet(activeSheetName)

		// Set active sheet of the workbook.
		f.SetActiveSheet(index)
		// Set tabletitle value of a cell.
		tableInfo := map[string]string{
			"A1": "Id",
			"B1": "Filename",
			"C1": "Product",
			"D1": "Fofaquery",
		}
		for k, v := range tableInfo {
			f.SetCellValue(activeSheetName, k, v)
		}
	} else { // 追加写入excel
		f, _ = excelize.OpenFile(fileNamePath)
		rows, _ := f.GetRows(activeSheetName)
		lastLineNum = len(rows) //找到最后一行
	}
	// Set table content value of a cell.
	for index, list := range *lists {
		if !exists || err != nil {
			//如果不存在从第2行写入
			rowNum = index + 2
		} else {
			//否则从文件内容尾行写入
			rowNum = lastLineNum + index + 1
		}
		f.SetCellValue(activeSheetName, fmt.Sprintf("A%d", rowNum), list.Id)
		f.SetCellValue(activeSheetName, fmt.Sprintf("B%d", rowNum), list.Filename)
		f.SetCellValue(activeSheetName, fmt.Sprintf("C%d", rowNum), list.Product)
		f.SetCellValue(activeSheetName, fmt.Sprintf("D%d", rowNum), list.Fofaquery)
	}
	// Save spreadsheet by the given path.  static/downloads/Book1.xlsx
	if err := f.SaveAs(fileNamePath); err != nil {
		fmt.Println(err)
		return errors.New(fmt.Sprintf("save file failed, path:(%s)", fileNamePath))
	}
	return nil
}

封装

/*
	dirPath := "log"
	fileName := "Book1.xlsx"
	dataList := [][]interface{}{{"姓名", "电话", "公司", "职位", "加入时间"}, {1, 2, "刘犇,刘犇,刘犇", "4", "5"}}
	AppendSaveExcel(dirPath, fileName, &dataList) //会存到log/Book1.xlsx里
   文件没有创建并写入,有追加写
*/
func AppendSaveExcel(dirPath, fileName string, dataList *[][]interface{}) (err error) {
	if len(*dataList) == 0 {
		return errors.New("数据不能为空")
	}
	//不存在创建目录
	_ = CreateFolder(dirPath, true)
	activeSheetName := "Sheet1"
	//文件路径
	fileNamePath := path.Join(dirPath, fileName)
	// 从第几行开始写数据
	rowNum := 0
	// excel最后数据所有行数
	lastLineNum := 0
	var f *excelize.File
	// 创建excel
	//判断文件是否存在,不存在新建
	fileExistsBool := FileExists(fileNamePath)
	if !fileExistsBool {
		f = excelize.NewFile()
	} else { // 追加写入excel
		f, _ = excelize.OpenFile(fileNamePath)
		rows, _ := f.GetRows(activeSheetName)
		lastLineNum = len(rows) //找到最后一行
	}
	// Create a new sheet.
	index := f.NewSheet(activeSheetName)

	// 设置工作簿的默认工作表
	f.SetActiveSheet(index)

	//设置列宽度为16
	var ColAbc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	//需要存入数据的列长度
	dataColLen := len((*dataList)[0])
	if dataColLen > 26 {
		err = f.SetColWidth("Sheet1", "A", "Z", 16)
	} else {
		err = f.SetColWidth("Sheet1", "A", ColAbc[dataColLen-1:dataColLen], 16)
	}
	if err != nil {
		fmt.Println(err)
		return errors.New(fmt.Sprintf("f.SetColWidth failed, err:%v", err))
	}
	// 从文件内容尾行写入
	rowNum = lastLineNum
	// 循环按行赋值
	for _, list := range *dataList {
		rowNum += 1
		//按行赋值 从aN开始按行赋值
		f.SetSheetRow(activeSheetName, fmt.Sprintf("A%d", rowNum), &list)
	}
	// 保存excel
	if err := f.SaveAs(fileNamePath); err != nil {
		fmt.Println(err)
		return errors.New(fmt.Sprintf("save file failed, path:(%s)", fileNamePath))
	}
	return nil
}

func CreateFolder(p string, ignoreExists bool) error {
	if FolderExists(p) == true && ignoreExists == false {
		err := errors.New("folder exists")
		return err
	}
	if FolderExists(p) == false {
		err := os.MkdirAll(p, os.ModePerm)
		if err != nil {
			return err
		}
	}
	return nil
}

func FileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	if info == nil {
		return false
	}
	return true
}