国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Go】excelize庫(kù)實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出封裝(一),自定義導(dǎo)出樣式、隔行背景色、自適應(yīng)行高、動(dòng)態(tài)導(dǎo)出指定列、動(dòng)態(tài)更改表頭

這篇具有很好參考價(jià)值的文章主要介紹了【Go】excelize庫(kù)實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出封裝(一),自定義導(dǎo)出樣式、隔行背景色、自適應(yīng)行高、動(dòng)態(tài)導(dǎo)出指定列、動(dòng)態(tài)更改表頭。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

前言

最近在學(xué)go操作excel,畢竟在web開(kāi)發(fā)里,操作excel是非常非常常見(jiàn)的。這里我選擇用 excelize 庫(kù)來(lái)實(shí)現(xiàn)操作excel。

為了方便和通用,我們需要把導(dǎo)入導(dǎo)出進(jìn)行封裝,這樣以后就可以很方便的拿來(lái)用,或者進(jìn)行擴(kuò)展。

go 導(dǎo)出excel,Go,excelize,golang,excel

我參考的是這篇文章:【GO】excelize導(dǎo)入導(dǎo)出封裝

功能

這個(gè)導(dǎo)入導(dǎo)出封裝,除了基本的導(dǎo)入導(dǎo)出,我還需要一些其他功能。例如:設(shè)置隔行背景色、自適應(yīng)行高、忽略指定字段或?qū)С鲋付ㄗ侄?、?fù)雜表頭 等等。

因?yàn)閷?shí)際項(xiàng)目中,操作excel不可能只是導(dǎo)出一個(gè)很簡(jiǎn)單的excel,實(shí)際項(xiàng)目中的要求往往要復(fù)雜的多。

go 導(dǎo)出excel,Go,excelize,golang,excel

導(dǎo)入

導(dǎo)入有以下幾個(gè)通用的實(shí)現(xiàn)

  • 導(dǎo)入單個(gè)sheet的數(shù)據(jù)(已完成)
  • 導(dǎo)入指定sheet的數(shù)據(jù)(已完成)
  • 導(dǎo)入多個(gè)sheet的數(shù)據(jù)(已完成)

導(dǎo)出

導(dǎo)出呢,就要復(fù)雜很多了,一級(jí)表頭的普通導(dǎo)出是最簡(jiǎn)單的,實(shí)際項(xiàng)目中往往還會(huì)有多級(jí)表頭,然后不管是一級(jí)還是多級(jí)表頭,還需要有各種要求的樣式,隔行背景色、自適應(yīng)行高這種已經(jīng)算簡(jiǎn)單的了,復(fù)雜點(diǎn)的還有一對(duì)多的縱向單元格合并。

所以導(dǎo)出需要實(shí)現(xiàn)以下這些:

  • 普通導(dǎo)出(已完成)
    • 一級(jí)表頭
    • 單個(gè)sheet
  • 復(fù)雜表頭、樹(shù)形結(jié)構(gòu)表頭導(dǎo)出(未完成)
  • 多個(gè)sheet導(dǎo)出(未完成)
  • 基于map導(dǎo)出(未完成)
  • 一對(duì)多縱向合并單元格(未完成)
  • 動(dòng)態(tài)導(dǎo)出列(已完成)
    • 忽略指定字段
    • 導(dǎo)出指定字段
    • 動(dòng)態(tài)更改表頭名稱
  • 隔行背景色樣式(已完成)
  • 自適應(yīng)行高樣式(已完成)

這篇文章我們就來(lái)實(shí)現(xiàn)那幾個(gè)已完成(未完成的還沒(méi)開(kāi)始實(shí)現(xiàn)呢,還有好多沒(méi)實(shí)現(xiàn),哭了)

go 導(dǎo)出excel,Go,excelize,golang,excel

其實(shí)上面這些功能,我之前早就在Java中實(shí)現(xiàn)了。感興趣的話可以去這篇文章看看,有完整代碼:

poi+easypoi實(shí)現(xiàn)表頭多層循環(huán),多級(jí)動(dòng)態(tài)表頭、樹(shù)形結(jié)構(gòu)動(dòng)態(tài)表頭、縱向合并單元格、多個(gè)sheet導(dǎo)出

實(shí)現(xiàn)

我們先在項(xiàng)目中,創(chuàng)建一個(gè)excel文件夾,里面放的就是我們封裝的實(shí)現(xiàn)函數(shù)

go 導(dǎo)出excel,Go,excelize,golang,excel

準(zhǔn)備

既然是通用的導(dǎo)入導(dǎo)出,那每次導(dǎo)入導(dǎo)出不同表格時(shí),不可能說(shuō)寫死導(dǎo)入哪些列(列名),而是應(yīng)該是按照不同表格對(duì)應(yīng)的不同結(jié)構(gòu)體來(lái)進(jìn)行解析數(shù)據(jù)或?qū)С鰯?shù)據(jù)。

所以我們可以定義一個(gè)專門用于解析excel的tag結(jié)構(gòu)體(類似于easypoi的@Excel注解),在這個(gè)tag結(jié)構(gòu)體定義幾個(gè)字段,什么表頭名稱、列下標(biāo)、列寬啊這些

用的時(shí)候呢,就是在不同結(jié)構(gòu)體中,使用反引號(hào)去定義 表頭名稱、列下標(biāo)、列寬 這些的值。

excel.go

自定義一個(gè)tag結(jié)構(gòu)體

package excel

import (
	"github.com/pkg/errors"
	"github.com/xuri/excelize/v2"
	"regexp"
	"strconv"
	"strings"
)

// 定義正則表達(dá)式模式
const (
	ExcelTagKey = "excel"
	Pattern     = "name:(.*?);|index:(.*?);|width:(.*?);|needMerge:(.*?);|replace:(.*?);"
)

type ExcelTag struct {
	Value     interface{}
	Name      string // 表頭標(biāo)題
	Index     int    // 列下標(biāo)(從0開(kāi)始)
	Width     int    // 列寬
	NeedMerge bool   // 是否需要合并
	Replace   string // 替換(需要替換的內(nèi)容_替換后的內(nèi)容。比如:1_未開(kāi)始 ==> 表示1替換為未開(kāi)始)
}

// 構(gòu)造函數(shù),返回一個(gè)帶有默認(rèn)值的 ExcelTag 實(shí)例
func NewExcelTag() ExcelTag {
	return ExcelTag{
		// 導(dǎo)入時(shí)會(huì)根據(jù)這個(gè)下標(biāo)來(lái)拿單元格的值,當(dāng)目標(biāo)結(jié)構(gòu)體字段沒(méi)有設(shè)置index時(shí),
		// 解析字段tag值時(shí)Index沒(méi)讀到就一直默認(rèn)為0,拿單元格的值時(shí),就始終拿的是第一列的值
		Index: -1, // 設(shè)置 Index 的默認(rèn)值為 -1
	}
}

定義好了tag結(jié)構(gòu)體,我們還需要給它綁定解析tag的方法

// 讀取字段tag值
func (e *ExcelTag) GetTag(tag string) (err error) {
	// 編譯正則表達(dá)式
	re := regexp.MustCompile(Pattern)
	matches := re.FindAllStringSubmatch(tag, -1)
	if len(matches) > 0 {
		for _, match := range matches {
			for i, val := range match {
				if i != 0 && val != "" {
					e.setValue(match, val)
				}
			}
		}
	} else {
		err = errors.New("未匹配到值")
		return
	}
	return
}

// 設(shè)置ExcelTag 對(duì)應(yīng)字段的值
func (e *ExcelTag) setValue(tag []string, value string) {
	if strings.Contains(tag[0], "name") {
		e.Name = value
	}
	if strings.Contains(tag[0], "index") {
		v, _ := strconv.ParseInt(value, 10, 8)
		e.Index = int(v)
	}
	if strings.Contains(tag[0], "width") {
		v, _ := strconv.ParseInt(value, 10, 8)
		e.Width = int(v)
	}
	if strings.Contains(tag[0], "needMerge") {
		v, _ := strconv.ParseBool(value)
		e.NeedMerge = v
	}
	if strings.Contains(tag[0], "replace") {
		e.Replace = value
	}
}

用的時(shí)候,比如在某個(gè)用戶信息結(jié)構(gòu)體中

go 導(dǎo)出excel,Go,excelize,golang,excel

自定義一個(gè)excel對(duì)象結(jié)構(gòu)體

定義好了tag結(jié)構(gòu)體,同樣是在 excel.go 文件中,我們還需要一個(gè)excel對(duì)象結(jié)構(gòu)體,里面有excel file對(duì)象、樣式等屬性,然后再給它綁定設(shè)置樣式的方法。

type Excel struct {
	F             *excelize.File // excel 對(duì)象
	TitleStyle    int            // 表頭樣式
	HeadStyle     int            // 表頭樣式
	ContentStyle1 int            // 主體樣式1,無(wú)背景色
	ContentStyle2 int            // 主體樣式2,有背景色
}

// 初始化
func ExcelInit() (e *Excel) {
	e = &Excel{}
	// excel構(gòu)建
	e.F = excelize.NewFile()
	// 初始化樣式
	e.getTitleRowStyle()
	e.getHeadRowStyle()
	e.getDataRowStyle()
	return e
}

// 獲取邊框樣式
func getBorder() []excelize.Border {
	return []excelize.Border{ // 邊框
		{Type: "top", Color: "000000", Style: 1},
		{Type: "bottom", Color: "000000", Style: 1},
		{Type: "left", Color: "000000", Style: 1},
		{Type: "right", Color: "000000", Style: 1},
	}
}

// 標(biāo)題樣式
func (e *Excel) getTitleRowStyle() {
	e.TitleStyle, _ = e.F.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{ // 對(duì)齊方式
			Horizontal: "center", // 水平對(duì)齊居中
			Vertical:   "center", // 垂直對(duì)齊居中
		},
		Fill: excelize.Fill{ // 背景顏色
			Type:    "pattern",
			Color:   []string{"#fff2cc"},
			Pattern: 1,
		},
		Font: &excelize.Font{ // 字體
			Bold: true,
			Size: 16,
		},
		Border: getBorder(),
	})
}

// 列頭行樣式
func (e *Excel) getHeadRowStyle() {
	e.HeadStyle, _ = e.F.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{ // 對(duì)齊方式
			Horizontal: "center", // 水平對(duì)齊居中
			Vertical:   "center", // 垂直對(duì)齊居中
			WrapText:   true,     // 自動(dòng)換行
		},
		Fill: excelize.Fill{ // 背景顏色
			Type:    "pattern",
			Color:   []string{"#FDE9D8"},
			Pattern: 1,
		},
		Font: &excelize.Font{ // 字體
			Bold: true,
			Size: 14,
		},
		Border: getBorder(),
	})
}

// 數(shù)據(jù)行樣式
func (e *Excel) getDataRowStyle() {
	style := excelize.Style{}
	style.Border = getBorder()
	style.Alignment = &excelize.Alignment{
		Horizontal: "center", // 水平對(duì)齊居中
		Vertical:   "center", // 垂直對(duì)齊居中
		WrapText:   true,     // 自動(dòng)換行
	}
	style.Font = &excelize.Font{
		Size: 12,
	}
	e.ContentStyle1, _ = e.F.NewStyle(&style)
	style.Fill = excelize.Fill{ // 背景顏色
		Type:    "pattern",
		Color:   []string{"#cce7f5"},
		Pattern: 1,
	}
	e.ContentStyle2, _ = e.F.NewStyle(&style)
}

導(dǎo)入

接下來(lái)我們就可以來(lái)實(shí)現(xiàn)導(dǎo)入函數(shù)的封裝了,在 excel_import.go 文件中

package excel

import (
	"github.com/pkg/errors"
	"github.com/xuri/excelize/v2"
	"go-web/util"
	"reflect"
	"strconv"
)

// ImportExcel 導(dǎo)入數(shù)據(jù)(單個(gè)sheet)
// 需要在傳入的結(jié)構(gòu)體中的字段加上tag:excel:"title:列頭名稱;"
// f 獲取到的excel對(duì)象、dst 導(dǎo)入目標(biāo)對(duì)象【傳指針】
// headIndex 表頭的索引,從0開(kāi)始(用于獲取表頭名字)
// startRow 頭行行數(shù)(從第startRow+1行開(kāi)始掃)
func ImportExcel(f *excelize.File, dst interface{}, headIndex, startRow int) (err error) {
	sheetName := f.GetSheetName(0) // 單個(gè)sheet時(shí),默認(rèn)讀取第一個(gè)sheet
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// ImportBySheet 導(dǎo)入數(shù)據(jù)(讀取指定sheet)sheetName Sheet名稱
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
	// 當(dāng)需要讀取多個(gè)sheet時(shí),可以通過(guò)下面的方式,來(lái)調(diào)用 ImportBySheet 這個(gè)函數(shù)
	//sheetList := f.GetSheetList()
	//for _, sheetName := range sheetList {
	//	ImportBySheet(f,dst,sheetName,headIndex,startRow)
	//}
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// 解析數(shù)據(jù)
func importData(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
	rows, err := f.GetRows(sheetName) // 獲取所有行
	if err != nil {
		err = errors.New(sheetName + "工作表不存在")
		return
	}
	dataValue := reflect.ValueOf(dst) // 取目標(biāo)對(duì)象的元素類型、字段類型和 tag
	// 判斷數(shù)據(jù)的類型
	if dataValue.Kind() != reflect.Ptr || dataValue.Elem().Kind() != reflect.Slice {
		err = errors.New("Invalid data type")
	}
	heads := []string{}                        // 表頭
	dataType := dataValue.Elem().Type().Elem() // 獲取導(dǎo)入目標(biāo)對(duì)象的類型信息
	// 遍歷行,解析數(shù)據(jù)并填充到目標(biāo)對(duì)象中
	for rowIndex, row := range rows {
		if rowIndex == headIndex {
			heads = row
		}
		if rowIndex < startRow { // 跳過(guò)頭行
			continue
		}
		newData := reflect.New(dataType).Elem() // 創(chuàng)建新的目標(biāo)對(duì)象
		// 遍歷目標(biāo)對(duì)象的字段
		for i := 0; i < dataType.NumField(); i++ {
			// 這里要用構(gòu)造函數(shù),構(gòu)造函數(shù)里指定了Index默認(rèn)值為-1,當(dāng)目標(biāo)結(jié)構(gòu)體的tag沒(méi)有指定index的話,那么 excelTag.Index 就一直為0
			// 那么 row[excelizeIndex] 就始終是 row[0],始終拿的是第一列的數(shù)據(jù)
			var excelTag = NewExcelTag()
			field := dataType.Field(i) // 獲取字段信息和tag
			tag := field.Tag.Get(ExcelTagKey)
			if tag == "" { // 如果tag不存在,則跳過(guò)
				continue
			}
			err = excelTag.GetTag(tag)
			if err != nil {
				return
			}
			cellValue := ""
			if excelTag.Index >= 0 { // 當(dāng)tag里指定了index時(shí),根據(jù)這個(gè)index來(lái)拿數(shù)據(jù)
				excelizeIndex := excelTag.Index // 解析tag的值
				if excelizeIndex >= len(row) {  // 防止下標(biāo)越界
					continue
				}
				cellValue = row[excelizeIndex] // 獲取單元格的值
			} else { // 否則根據(jù)表頭名稱來(lái)拿數(shù)據(jù)
				if util.IsContain(heads, excelTag.Name) { // 當(dāng)tag里的表頭名稱和excel表格里面的表頭名稱相匹配時(shí)
					if i >= len(row) { // 防止下標(biāo)越界
						continue
					}
					cellValue = row[i] // 獲取單元格的值
				}
			}
			// 根據(jù)字段類型設(shè)置值
			switch field.Type.Kind() {
			case reflect.Int:
				v, _ := strconv.ParseInt(cellValue, 10, 64)
				newData.Field(i).SetInt(v)
			case reflect.String:
				newData.Field(i).SetString(cellValue)
			}
		}
		// 將新的目標(biāo)對(duì)象添加到導(dǎo)入目標(biāo)對(duì)象的slice中
		dataValue.Elem().Set(reflect.Append(dataValue.Elem(), newData))
	}
	return
}

導(dǎo)入這里用到了一個(gè) IsContain 函數(shù),代碼如下:

// 判斷數(shù)組中是否包含指定元素
func IsContain(items interface{}, item interface{}) bool {
	switch items.(type) {
	case []int:
		intArr := items.([]int)
		for _, value := range intArr {
			if value == item.(int) {
				return true
			}
		}
	case []string:
		strArr := items.([]string)
		for _, value := range strArr {
			if value == item.(string) {
				return true
			}
		}
	default:
		return false
	}
	return false
}

導(dǎo)出

excel_export.go 文件中

package excel

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/xuri/excelize/v2"
	"net/http"
	"reflect"
	"sort"
	"strings"
)

// GetExcelColumnName 根據(jù)列數(shù)生成 Excel 列名
func GetExcelColumnName(columnNumber int) string {
	columnName := ""
	for columnNumber > 0 {
		columnNumber--
		columnName = string('A'+columnNumber%26) + columnName
		columnNumber /= 26
	}
	return columnName
}

// ExportExcel excel導(dǎo)出
func ExportExcel(sheet, title, fields string, isGhbj, isIgnore bool, list interface{}, changeHead map[string]string, e *Excel) (err error) {
	index, _ := e.F.GetSheetIndex(sheet)
	if index < 0 { // 如果sheet名稱不存在
		e.F.NewSheet(sheet)
	}
	// 構(gòu)造excel表格
	// 取目標(biāo)對(duì)象的元素類型、字段類型和 tag
	dataValue := reflect.ValueOf(list)
	// 判斷數(shù)據(jù)的類型
	if dataValue.Kind() != reflect.Slice {
		err = errors.New("invalid data type")
		return
	}
	// 構(gòu)造表頭
	endColName, dataRow, err := normalBuildTitle(e, sheet, title, fields, isIgnore, changeHead, dataValue)
	if err != nil {
		return
	}
	// 構(gòu)造數(shù)據(jù)行
	err = normalBuildDataRow(e, sheet, endColName, fields, dataRow, isGhbj, isIgnore, dataValue)
	return
}

// ================================= 普通導(dǎo)出 =================================

// NormalDownLoad 導(dǎo)出excel并下載(單個(gè)sheet)
func NormalDownLoad(fileName, sheet, title string, isGhbj bool, list interface{}, res http.ResponseWriter) error {
	f, err := NormalDynamicExport(list, sheet, title, "", isGhbj, false, nil)
	if err != nil {
		return err
	}
	DownLoadExcel(fileName, res, f)
	return nil
}

// NormalDynamicDownLoad 動(dòng)態(tài)導(dǎo)出excel并下載(單個(gè)sheet)
// isIgnore 是否忽略指定字段(true 要忽略的字段 false 要導(dǎo)出的字段)
// fields 選擇的字段,多個(gè)字段用逗號(hào)隔開(kāi),最后一個(gè)字段后面也要加逗號(hào),如:字段1,字段2,字段3,
// changeHead 要改變表頭的字段,格式是{"字段1":"更改的表頭1","字段2":"更改的表頭2"}
func NormalDynamicDownLoad(fileName, sheet, title, fields string, isGhbj, isIgnore bool,
	list interface{}, changeHead map[string]string, res http.ResponseWriter) error {
	f, err := NormalDynamicExport(list, sheet, title, fields, isGhbj, isIgnore, changeHead)
	if err != nil {
		return err
	}
	DownLoadExcel(fileName, res, f)
	return nil
}

// NormalDynamicExport 導(dǎo)出excel
// ** 需要在傳入的結(jié)構(gòu)體中的字段加上tag:excelize:"title:列頭名稱;index:列下標(biāo)(從0開(kāi)始);"
// list 需要導(dǎo)出的對(duì)象數(shù)組、sheet sheet名稱、title 標(biāo)題、isGhbj 是否設(shè)置隔行背景色
func NormalDynamicExport(list interface{}, sheet, title, fields string, isGhbj, isIgnore bool, changeHead map[string]string) (file *excelize.File, err error) {
	e := ExcelInit()
	err = ExportExcel(sheet, title, fields, isGhbj, isIgnore, list, changeHead, e)
	if err != nil {
		return
	}
	return e.F, err
}

// 構(gòu)造表頭(endColName 最后一列的列名 dataRow 數(shù)據(jù)行開(kāi)始的行號(hào))
func normalBuildTitle(e *Excel, sheet, title, fields string, isIgnore bool, changeHead map[string]string, dataValue reflect.Value) (endColName string, dataRow int, err error) {
	dataType := dataValue.Type().Elem() // 獲取導(dǎo)入目標(biāo)對(duì)象的類型信息
	var exportTitle []ExcelTag          // 遍歷目標(biāo)對(duì)象的字段
	for i := 0; i < dataType.NumField(); i++ {
		var excelTag ExcelTag
		field := dataType.Field(i) // 獲取字段信息和tag
		tag := field.Tag.Get(ExcelTagKey)
		if tag == "" { // 如果非導(dǎo)出則跳過(guò)
			continue
		}
		if fields != "" { // 選擇要導(dǎo)出或要忽略的字段
			if isIgnore && strings.Contains(fields, field.Name+",") { // 忽略指定字段
				continue
			}
			if !isIgnore && !strings.Contains(fields, field.Name+",") { // 導(dǎo)出指定字段
				continue
			}
		}
		err = excelTag.GetTag(tag)
		if err != nil {
			return
		}
		// 更改指定字段的表頭標(biāo)題
		if changeHead != nil && changeHead[field.Name] != "" {
			excelTag.Name = changeHead[field.Name]
		}
		exportTitle = append(exportTitle, excelTag)
	}
	// 排序
	sort.Slice(exportTitle, func(i, j int) bool {
		return exportTitle[i].Index < exportTitle[j].Index
	})
	var titleRowData []interface{} // 列頭行
	for i, colTitle := range exportTitle {
		endColName := GetExcelColumnName(i + 1)
		if colTitle.Width > 0 { // 根據(jù)給定的寬度設(shè)置列寬
			_ = e.F.SetColWidth(sheet, endColName, endColName, float64(colTitle.Width))
		} else {
			_ = e.F.SetColWidth(sheet, endColName, endColName, float64(20)) // 默認(rèn)寬度為20
		}
		titleRowData = append(titleRowData, colTitle.Name)
	}
	endColName = GetExcelColumnName(len(titleRowData)) // 根據(jù)列數(shù)生成 Excel 列名
	if title != "" {
		dataRow = 3 // 如果有title,那么從第3行開(kāi)始就是數(shù)據(jù)行,第1行是title,第2行是表頭
		e.F.SetCellValue(sheet, "A1", title)
		e.F.MergeCell(sheet, "A1", endColName+"1") // 合并標(biāo)題單元格
		e.F.SetCellStyle(sheet, "A1", endColName+"1", e.TitleStyle)
		e.F.SetRowHeight(sheet, 1, float64(30)) // 第一行行高
		e.F.SetRowHeight(sheet, 2, float64(30)) // 第二行行高
		e.F.SetCellStyle(sheet, "A2", endColName+"2", e.HeadStyle)
		if err = e.F.SetSheetRow(sheet, "A2", &titleRowData); err != nil {
			return
		}
	} else {
		dataRow = 2 // 如果沒(méi)有title,那么從第2行開(kāi)始就是數(shù)據(jù)行,第1行是表頭
		e.F.SetRowHeight(sheet, 1, float64(30))
		e.F.SetCellStyle(sheet, "A1", endColName+"1", e.HeadStyle)
		if err = e.F.SetSheetRow(sheet, "A1", &titleRowData); err != nil {
			return
		}
	}
	return
}

// 構(gòu)造數(shù)據(jù)行
func normalBuildDataRow(e *Excel, sheet, endColName, fields string, row int, isGhbj, isIgnore bool, dataValue reflect.Value) (err error) {
	//實(shí)時(shí)寫入數(shù)據(jù)
	for i := 0; i < dataValue.Len(); i++ {
		startCol := fmt.Sprintf("A%d", row)
		endCol := fmt.Sprintf("%s%d", endColName, row)
		item := dataValue.Index(i)
		typ := item.Type()
		num := item.NumField()
		var exportRow []ExcelTag
		maxLen := 0 // 記錄這一行中,數(shù)據(jù)最多的單元格的值的長(zhǎng)度
		//遍歷結(jié)構(gòu)體的所有字段
		for j := 0; j < num; j++ {
			dataField := typ.Field(j) //獲取到struct標(biāo)簽,需要通過(guò)reflect.Type來(lái)獲取tag標(biāo)簽的值
			tagVal := dataField.Tag.Get(ExcelTagKey)
			if tagVal == "" { // 如果非導(dǎo)出則跳過(guò)
				continue
			}
			if fields != "" { // 選擇要導(dǎo)出或要忽略的字段
				if isIgnore && strings.Contains(fields, dataField.Name+",") { // 忽略指定字段
					continue
				}
				if !isIgnore && !strings.Contains(fields, dataField.Name+",") { // 導(dǎo)出指定字段
					continue
				}
			}
			var dataCol ExcelTag
			err = dataCol.GetTag(tagVal)
			fieldData := item.FieldByName(dataField.Name) // 取字段值
			if fieldData.Type().String() == "string" {    // string類型的才計(jì)算長(zhǎng)度
				rwsTemp := fieldData.Len() // 當(dāng)前單元格內(nèi)容的長(zhǎng)度
				if rwsTemp > maxLen {      //這里取每一行中的每一列字符長(zhǎng)度最大的那一列的字符
					maxLen = rwsTemp
				}
			}
			// 替換
			if dataCol.Replace != "" {
				split := strings.Split(dataCol.Replace, ",")
				for j := range split {
					s := strings.Split(split[j], "_") // 根據(jù)下劃線進(jìn)行分割,格式:需要替換的內(nèi)容_替換后的內(nèi)容
					value := fieldData.String()
					if strings.Contains(fieldData.Type().String(), "int") {
						value = strconv.Itoa(int(fieldData.Int()))
					} else if fieldData.Type().String() == "bool" {
						value = strconv.FormatBool(fieldData.Bool())
					} else if strings.Contains(fieldData.Type().String(), "float") {
						value = strconv.FormatFloat(fieldData.Float(), 'f', -1, 64)
					}
					if s[0] == value {
						dataCol.Value = s[1]
					}
				}
			} else {
				dataCol.Value = fieldData
			}
			if err != nil {
				return
			}
			exportRow = append(exportRow, dataCol)
		}
		// 排序
		sort.Slice(exportRow, func(i, j int) bool {
			return exportRow[i].Index < exportRow[j].Index
		})
		var rowData []interface{} // 數(shù)據(jù)列
		for _, colTitle := range exportRow {
			rowData = append(rowData, colTitle.Value)
		}
		if isGhbj && row%2 == 0 {
			_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle2)
		} else {
			_ = e.F.SetCellStyle(sheet, startCol, endCol, e.ContentStyle1)
		}
		if maxLen > 25 { // 自適應(yīng)行高
			d := maxLen / 25
			f := 25 * d
			_ = e.F.SetRowHeight(sheet, row, float64(f))
		} else {
			_ = e.F.SetRowHeight(sheet, row, float64(25)) // 默認(rèn)行高25
		}
		if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
			return
		}
		row++
	}
	return
}


// 下載
func DownLoadExcel(fileName string, res http.ResponseWriter, file *excelize.File) {
	// 設(shè)置響應(yīng)頭
	res.Header().Set("Content-Type", "text/html; charset=UTF-8")
	res.Header().Set("Content-Type", "application/octet-stream")
	res.Header().Set("Content-Disposition", "attachment; filename="+fileName+".xlsx")
	res.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
	err := file.Write(res) // 寫入Excel文件內(nèi)容到響應(yīng)體
	if err != nil {
		http.Error(res, err.Error(), http.StatusInternalServerError)
		return
	}
}

測(cè)試

ok,終于寫完了導(dǎo)入導(dǎo)出,接下來(lái)就是測(cè)試?yán)?/p>

go 導(dǎo)出excel,Go,excelize,golang,excel

excel_main.go 文件中

package main

import (
	"fmt"
	"github.com/xuri/excelize/v2"
	"go-web/util/excel"
)

func main() {
	//export()
	imports()
}

type Test struct {
	Id       string `excel:"name:用戶賬號(hào);"`
	Name     string `excel:"name:用戶姓名;index:1;"`
	Email    string `excel:"name:用戶郵箱;width:25;"`
	Com      string `excel:"name:所屬公司;"`
	Dept     string `excel:"name:所在部門;"`
	RoleKey  string `excel:"name:角色代碼;"`
	RoleName string `excel:"name:角色名稱;replace:1_超級(jí)管理員,2_普通用戶;"`
	Remark   string `excel:"name:備注;width:40;"`
}

// 導(dǎo)出
func export() {
	var testList = []Test{
		{"fuhua", "符華", "fuhua@123.com", "太虛劍派", "開(kāi)發(fā)部", "CJGLY", "1", "備注備注"},
		{"baiye", "白夜", "baiye@123.com", "天命科技有限公司", "執(zhí)行部", "PTYG", "2", ""},
		{"chiling", "熾翎", "chiling@123.com", "太虛劍派", "行政部", "PTYG", "2", "備注備注備注備注"},
		{"yunmo", "云墨", "yunmo@123.com", "太虛劍派", "財(cái)務(wù)部", "CJGLY", "1", ""},
		{"yuelun", "月輪", "yuelun@123.com", "天命科技有限公司", "執(zhí)行部", "CJGLY", "1", ""},
		{"xunyu", "迅羽",
			"xunyu@123.com哈哈哈哈哈哈哈哈這里是最大行高測(cè)試哈哈哈哈哈哈哈哈這11111111111里是最大行高測(cè)試哈哈哈哈哈哈哈哈這里是最大行高測(cè)試",
			"天命科技有限公司", "開(kāi)發(fā)部", "PTYG", "2",
			"備注備注備注備注com哈哈哈哈哈哈哈哈這里是最大行高測(cè)試哈哈哈哈哈哈哈哈這里是最大行高測(cè)試哈哈哈哈哈哈哈哈這里是最大行高測(cè)里是最大行高測(cè)試哈哈哈哈哈哈哈哈這里是最大行高測(cè)試"},
	}
	changeHead := map[string]string{"Id": "賬號(hào)", "Name": "真實(shí)姓名"}
	//f, err := excel.NormalExport(testList, "Sheet1", "用戶信息", "Id,Email,", true, true, changeHead)
	f, err := excel.NormalDynamicExport(testList, "Sheet1", "用戶信息", "", true, false, changeHead)
	if err != nil {
		fmt.Println(err)
		return
	}
	f.Path = "C:\\Users\\Administrator\\Desktop\\測(cè)試.xlsx"
	if err := f.Save(); err != nil {
		fmt.Println(err)
		return
	}
}

// 導(dǎo)入
func imports() {
	f, err := excelize.OpenFile("C:\\Users\\Administrator\\Desktop\\測(cè)試.xlsx")
	if err != nil {
		fmt.Println("文件打開(kāi)失敗")
	}
	importList := []Test{}
	err = excel.ImportExcel(f, &importList, 1, 2)
	if err != nil {
		fmt.Println(err)
	}
	for _, t := range importList {
		fmt.Println(t)
	}
}

實(shí)現(xiàn)效果

然后我們?cè)賮?lái)看看實(shí)現(xiàn)效果,說(shuō)實(shí)話,我覺(jué)得這表格還挺好看的哩,不愧是我

go 導(dǎo)出excel,Go,excelize,golang,excel

導(dǎo)出

go 導(dǎo)出excel,Go,excelize,golang,excel

導(dǎo)入

go 導(dǎo)出excel,Go,excelize,golang,excel

最后

這樣,我們就實(shí)現(xiàn)了一個(gè)通用的導(dǎo)入導(dǎo)出工具封裝。

上面這些就是全部代碼啦,后續(xù)等我把剩下幾個(gè)復(fù)雜導(dǎo)出弄完(挖坑…),我會(huì)把這些代碼全部抽出來(lái),做成一個(gè)獨(dú)立的組件模塊,然后上傳到Git上,這樣以后不管做哪個(gè)項(xiàng)目,用的時(shí)候直接在go.mod引入就可以啦~完美??
go 導(dǎo)出excel,Go,excelize,golang,excel

第二篇已更新:【Go】excelize庫(kù)實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出封裝(二),基于map、多個(gè)sheet、多級(jí)表頭、樹(shù)形結(jié)構(gòu)表頭導(dǎo)出,橫向、縱向合并單元格導(dǎo)出

好啦,以上就是本篇文章的全部?jī)?nèi)容了,如果你覺(jué)得對(duì)你有幫助或者覺(jué)得博主寫得不錯(cuò),千萬(wàn)不要吝嗇你的大拇指喲(^U^)ノ~YO

go 導(dǎo)出excel,Go,excelize,golang,excel文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-757580.html

到了這里,關(guān)于【Go】excelize庫(kù)實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出封裝(一),自定義導(dǎo)出樣式、隔行背景色、自適應(yīng)行高、動(dòng)態(tài)導(dǎo)出指定列、動(dòng)態(tài)更改表頭的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 前端vue導(dǎo)出excel(標(biāo)題加粗+表頭自定義樣式+表格邊框+單元格自定義樣式)

    前端vue導(dǎo)出excel(標(biāo)題加粗+表頭自定義樣式+表格邊框+單元格自定義樣式)

    接近過(guò)年,被一大堆excel報(bào)表煩死的我,遇到要求前端導(dǎo)出excel的后端,差點(diǎn)猝死的我拼命學(xué)習(xí)中,整理出這篇文章,希望看到這篇文章的你有所收獲,也希望能收到大佬們的指點(diǎn) 之前用c#,.net弄過(guò)導(dǎo)出word,excel,可以點(diǎn)擊查看.NET使用Aspose控件生成Word(可構(gòu)建自定義表格)、

    2024年04月15日
    瀏覽(29)
  • SpringBoot實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出

    SpringBoot實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出

    話不多說(shuō),直接上代碼 依賴文檔 找到pom文件,如下圖所示 引入需要的依賴 導(dǎo)出寫法: 導(dǎo)入寫法: 已上就是導(dǎo)入導(dǎo)出

    2024年02月09日
    瀏覽(22)
  • POI 實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出

    什么是POI Apache POI 是用Java編寫的免費(fèi)開(kāi)源的跨平臺(tái)的 Java API,Apache POI提供API給Java程序?qū)icrosoft Office格式檔案讀和寫的功能。POI為“Poor Obfuscation Implementation”的首字母縮寫,意為“簡(jiǎn)潔版的模糊實(shí)現(xiàn)”。 生成xls和xlsx有什么區(qū)別呢? XLS XLSX 只能打開(kāi)xls格式,無(wú)法直接打開(kāi)x

    2024年02月03日
    瀏覽(27)
  • JS實(shí)現(xiàn)Excel導(dǎo)入以及table導(dǎo)出為Excel

    在開(kāi)發(fā)項(xiàng)目的過(guò)程中遇到了一個(gè)需求,將excel文件導(dǎo)入并且解析渲染到頁(yè)面上。用戶可以對(duì)表格內(nèi)的部分內(nèi)容做修改后再上傳到服務(wù)端。 導(dǎo)入Excel 1.使用html支持上傳標(biāo)簽從本地獲取文件,例如type為file的input,el-upload等 2.實(shí)例化FileReader,并且通過(guò)readAsBinaryString將文件讀取為二

    2024年02月09日
    瀏覽(23)
  • 純前端實(shí)現(xiàn) 導(dǎo)入 與 導(dǎo)出 Excel

    純前端實(shí)現(xiàn) 導(dǎo)入 與 導(dǎo)出 Excel

    最近經(jīng)常在做 不規(guī)則 Excel 的導(dǎo)入,或者一些普通 Excel 的導(dǎo)出,當(dāng)前以上說(shuō)的都是純前端來(lái)實(shí)現(xiàn);下面我們來(lái)聊聊經(jīng)常用到的Excel導(dǎo)出與導(dǎo)入的實(shí)現(xiàn)方案,本文實(shí)現(xiàn)技術(shù)棧以 Vue2 + JS 為例 導(dǎo)入分類: 調(diào)用 API 完全由后端來(lái)解析數(shù)據(jù),清洗數(shù)據(jù),前端只負(fù)責(zé)調(diào)用 API ; 前端解析

    2024年02月09日
    瀏覽(28)
  • 使用luckysheet實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出

    使用luckysheet實(shí)現(xiàn)excel導(dǎo)入導(dǎo)出

    luckysheet-demo: luckysheet-demoexcel導(dǎo)入導(dǎo)出實(shí)例 使用組件 1.?luckysheet在線excel 2. luckyexcel excel導(dǎo)入插件 3. exceljs 導(dǎo)出excel數(shù)據(jù) 導(dǎo)出exceljs的二次封裝,可直接使用如下

    2024年02月12日
    瀏覽(19)
  • 使用EasyPoi實(shí)現(xiàn)Excel的按模板樣式導(dǎo)出

    使用EasyPoi實(shí)現(xiàn)Excel的按模板樣式導(dǎo)出

    1690342020350導(dǎo)出測(cè)試.xlsx 如下 #fe 使用#fe命令可以實(shí)現(xiàn)集合數(shù)據(jù)的橫向拓展,比如模板代碼是 導(dǎo)出的excel里面就會(huì)顯示會(huì)自當(dāng)前列,向右拓展,效果可參見(jiàn)下面的導(dǎo)出文件截圖 v_fe 使用v_fe命令可以實(shí)現(xiàn)不固定列的橫向遍歷,比如模板代碼是 分?jǐn)?shù) ID {{#fe:maths t.score t.id}} 這種情況

    2024年02月15日
    瀏覽(27)
  • EasyExcel實(shí)現(xiàn)Excel文件導(dǎo)入導(dǎo)出功能

    EasyExcel實(shí)現(xiàn)Excel文件導(dǎo)入導(dǎo)出功能

    Java領(lǐng)域解析、生成Excel比較有名的框架有Apache poi、jxl等。但他們都存在一個(gè)嚴(yán)重的問(wèn)題就是非常的耗內(nèi)存。如果你的系統(tǒng)并發(fā)量不大的話可能還行,但是一旦并發(fā)上來(lái)后一定會(huì)OOM或者JVM頻繁的full gc。 EasyExcel是阿里巴巴開(kāi)源的一個(gè)excel處理框架,以使用簡(jiǎn)單、節(jié)省內(nèi)存著稱。

    2024年02月02日
    瀏覽(23)
  • 使用EasyExcel實(shí)現(xiàn)Excel的導(dǎo)入導(dǎo)出

    在真實(shí)的開(kāi)發(fā)者場(chǎng)景中,經(jīng)常會(huì)使用excel作為數(shù)據(jù)的載體,進(jìn)行數(shù)據(jù)導(dǎo)入和導(dǎo)出的操作,使用excel的導(dǎo)入和導(dǎo)出有很多種解決方案,本篇記錄一下EasyExcel的使用。 EasyExcel是一個(gè)開(kāi)源的項(xiàng)目,是阿里開(kāi)發(fā)的。EasyExcel可以簡(jiǎn)化Excel表格的導(dǎo)入和導(dǎo)出操作,使用起來(lái)簡(jiǎn)單快捷,易上手

    2023年04月15日
    瀏覽(33)
  • SpringBoot實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出,簡(jiǎn)單好用

    SpringBoot實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出,簡(jiǎn)單好用

    POI是Java操作MicroOffice(如對(duì)Excel的導(dǎo)入導(dǎo)出)的一個(gè)插件。POI的全稱是(Poor Obfuscation Implementation),POI官網(wǎng)地址是 http://poi.achache.org/index.html 。 EasyPoi對(duì)POI進(jìn)行了優(yōu)化 ,更加設(shè)計(jì)精巧,使用簡(jiǎn)單,接口豐富,擴(kuò)展簡(jiǎn)單。EasyPOI的同類產(chǎn)品有Execel4J,Hutools等。EasyPoi官網(wǎng)地址是 h

    2024年02月11日
    瀏覽(26)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包