go 操作 Excel


文檔地址:

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
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM