【Go】excelize库实现excel导入导出封装(一),自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头

这篇具有很好参考价值的文章主要介绍了【Go】excelize库实现excel导入导出封装(一),自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近在学go操作excel,毕竟在web开发里,操作excel是非常非常常见的。这里我选择用 excelize 库来实现操作excel。

为了方便和通用,我们需要把导入导出进行封装,这样以后就可以很方便的拿来用,或者进行扩展。

go 导出excel,Go,excelize,golang,excel

我参考的是这篇文章:【GO】excelize导入导出封装

功能

这个导入导出封装,除了基本的导入导出,我还需要一些其他功能。例如:设置隔行背景色、自适应行高、忽略指定字段或导出指定字段、复杂表头 等等。

因为实际项目中,操作excel不可能只是导出一个很简单的excel,实际项目中的要求往往要复杂的多。

go 导出excel,Go,excelize,golang,excel

导入

导入有以下几个通用的实现

  • 导入单个sheet的数据(已完成)
  • 导入指定sheet的数据(已完成)
  • 导入多个sheet的数据(已完成)

导出

导出呢,就要复杂很多了,一级表头的普通导出是最简单的,实际项目中往往还会有多级表头,然后不管是一级还是多级表头,还需要有各种要求的样式,隔行背景色、自适应行高这种已经算简单的了,复杂点的还有一对多的纵向单元格合并。

所以导出需要实现以下这些:

  • 普通导出(已完成)
    • 一级表头
    • 单个sheet
  • 复杂表头、树形结构表头导出(未完成)
  • 多个sheet导出(未完成)
  • 基于map导出(未完成)
  • 一对多纵向合并单元格(未完成)
  • 动态导出列(已完成)
    • 忽略指定字段
    • 导出指定字段
    • 动态更改表头名称
  • 隔行背景色样式(已完成)
  • 自适应行高样式(已完成)

这篇文章我们就来实现那几个已完成(未完成的还没开始实现呢,还有好多没实现,哭了)

go 导出excel,Go,excelize,golang,excel

其实上面这些功能,我之前早就在Java中实现了。感兴趣的话可以去这篇文章看看,有完整代码:

poi+easypoi实现表头多层循环,多级动态表头、树形结构动态表头、纵向合并单元格、多个sheet导出

实现

我们先在项目中,创建一个excel文件夹,里面放的就是我们封装的实现函数

go 导出excel,Go,excelize,golang,excel

准备

既然是通用的导入导出,那每次导入导出不同表格时,不可能说写死导入哪些列(列名),而是应该是按照不同表格对应的不同结构体来进行解析数据或导出数据。

所以我们可以定义一个专门用于解析excel的tag结构体(类似于easypoi的@Excel注解),在这个tag结构体定义几个字段,什么表头名称、列下标、列宽啊这些

用的时候呢,就是在不同结构体中,使用反引号去定义 表头名称、列下标、列宽 这些的值。

excel.go

自定义一个tag结构体

package excel

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

// 定义正则表达式模式
const (
	ExcelTagKey = "excel"
	Pattern     = "name:(.*?);|index:(.*?);|width:(.*?);|needMerge:(.*?);|replace:(.*?);"
)

type ExcelTag struct {
	Value     interface{}
	Name      string // 表头标题
	Index     int    // 列下标(从0开始)
	Width     int    // 列宽
	NeedMerge bool   // 是否需要合并
	Replace   string // 替换(需要替换的内容_替换后的内容。比如:1_未开始 ==> 表示1替换为未开始)
}

// 构造函数,返回一个带有默认值的 ExcelTag 实例
func NewExcelTag() ExcelTag {
	return ExcelTag{
		// 导入时会根据这个下标来拿单元格的值,当目标结构体字段没有设置index时,
		// 解析字段tag值时Index没读到就一直默认为0,拿单元格的值时,就始终拿的是第一列的值
		Index: -1, // 设置 Index 的默认值为 -1
	}
}

定义好了tag结构体,我们还需要给它绑定解析tag的方法

// 读取字段tag值
func (e *ExcelTag) GetTag(tag string) (err error) {
	// 编译正则表达式
	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
}

// 设置ExcelTag 对应字段的值
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
	}
}

用的时候,比如在某个用户信息结构体中

go 导出excel,Go,excelize,golang,excel

自定义一个excel对象结构体

定义好了tag结构体,同样是在 excel.go 文件中,我们还需要一个excel对象结构体,里面有excel file对象、样式等属性,然后再给它绑定设置样式的方法。

type Excel struct {
	F             *excelize.File // excel 对象
	TitleStyle    int            // 表头样式
	HeadStyle     int            // 表头样式
	ContentStyle1 int            // 主体样式1,无背景色
	ContentStyle2 int            // 主体样式2,有背景色
}

// 初始化
func ExcelInit() (e *Excel) {
	e = &Excel{}
	// excel构建
	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},
	}
}

// 标题样式
func (e *Excel) getTitleRowStyle() {
	e.TitleStyle, _ = e.F.NewStyle(&excelize.Style{
		Alignment: &excelize.Alignment{ // 对齐方式
			Horizontal: "center", // 水平对齐居中
			Vertical:   "center", // 垂直对齐居中
		},
		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{ // 对齐方式
			Horizontal: "center", // 水平对齐居中
			Vertical:   "center", // 垂直对齐居中
			WrapText:   true,     // 自动换行
		},
		Fill: excelize.Fill{ // 背景颜色
			Type:    "pattern",
			Color:   []string{"#FDE9D8"},
			Pattern: 1,
		},
		Font: &excelize.Font{ // 字体
			Bold: true,
			Size: 14,
		},
		Border: getBorder(),
	})
}

// 数据行样式
func (e *Excel) getDataRowStyle() {
	style := excelize.Style{}
	style.Border = getBorder()
	style.Alignment = &excelize.Alignment{
		Horizontal: "center", // 水平对齐居中
		Vertical:   "center", // 垂直对齐居中
		WrapText:   true,     // 自动换行
	}
	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)
}

导入

接下来我们就可以来实现导入函数的封装了,在 excel_import.go 文件中

package excel

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

// ImportExcel 导入数据(单个sheet)
// 需要在传入的结构体中的字段加上tag:excel:"title:列头名称;"
// f 获取到的excel对象、dst 导入目标对象【传指针】
// headIndex 表头的索引,从0开始(用于获取表头名字)
// startRow 头行行数(从第startRow+1行开始扫)
func ImportExcel(f *excelize.File, dst interface{}, headIndex, startRow int) (err error) {
	sheetName := f.GetSheetName(0) // 单个sheet时,默认读取第一个sheet
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// ImportBySheet 导入数据(读取指定sheet)sheetName Sheet名称
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
	// 当需要读取多个sheet时,可以通过下面的方式,来调用 ImportBySheet 这个函数
	//sheetList := f.GetSheetList()
	//for _, sheetName := range sheetList {
	//	ImportBySheet(f,dst,sheetName,headIndex,startRow)
	//}
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// 解析数据
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) // 取目标对象的元素类型、字段类型和 tag
	// 判断数据的类型
	if dataValue.Kind() != reflect.Ptr || dataValue.Elem().Kind() != reflect.Slice {
		err = errors.New("Invalid data type")
	}
	heads := []string{}                        // 表头
	dataType := dataValue.Elem().Type().Elem() // 获取导入目标对象的类型信息
	// 遍历行,解析数据并填充到目标对象中
	for rowIndex, row := range rows {
		if rowIndex == headIndex {
			heads = row
		}
		if rowIndex < startRow { // 跳过头行
			continue
		}
		newData := reflect.New(dataType).Elem() // 创建新的目标对象
		// 遍历目标对象的字段
		for i := 0; i < dataType.NumField(); i++ {
			// 这里要用构造函数,构造函数里指定了Index默认值为-1,当目标结构体的tag没有指定index的话,那么 excelTag.Index 就一直为0
			// 那么 row[excelizeIndex] 就始终是 row[0],始终拿的是第一列的数据
			var excelTag = NewExcelTag()
			field := dataType.Field(i) // 获取字段信息和tag
			tag := field.Tag.Get(ExcelTagKey)
			if tag == "" { // 如果tag不存在,则跳过
				continue
			}
			err = excelTag.GetTag(tag)
			if err != nil {
				return
			}
			cellValue := ""
			if excelTag.Index >= 0 { // 当tag里指定了index时,根据这个index来拿数据
				excelizeIndex := excelTag.Index // 解析tag的值
				if excelizeIndex >= len(row) {  // 防止下标越界
					continue
				}
				cellValue = row[excelizeIndex] // 获取单元格的值
			} else { // 否则根据表头名称来拿数据
				if util.IsContain(heads, excelTag.Name) { // 当tag里的表头名称和excel表格里面的表头名称相匹配时
					if i >= len(row) { // 防止下标越界
						continue
					}
					cellValue = row[i] // 获取单元格的值
				}
			}
			// 根据字段类型设置值
			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)
			}
		}
		// 将新的目标对象添加到导入目标对象的slice中
		dataValue.Elem().Set(reflect.Append(dataValue.Elem(), newData))
	}
	return
}

导入这里用到了一个 IsContain 函数,代码如下:

// 判断数组中是否包含指定元素
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
}

导出

excel_export.go 文件中

package excel

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

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

// ExportExcel excel导出
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)
	}
	// 构造excel表格
	// 取目标对象的元素类型、字段类型和 tag
	dataValue := reflect.ValueOf(list)
	// 判断数据的类型
	if dataValue.Kind() != reflect.Slice {
		err = errors.New("invalid data type")
		return
	}
	// 构造表头
	endColName, dataRow, err := normalBuildTitle(e, sheet, title, fields, isIgnore, changeHead, dataValue)
	if err != nil {
		return
	}
	// 构造数据行
	err = normalBuildDataRow(e, sheet, endColName, fields, dataRow, isGhbj, isIgnore, dataValue)
	return
}

// ================================= 普通导出 =================================

// NormalDownLoad 导出excel并下载(单个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 动态导出excel并下载(单个sheet)
// isIgnore 是否忽略指定字段(true 要忽略的字段 false 要导出的字段)
// fields 选择的字段,多个字段用逗号隔开,最后一个字段后面也要加逗号,如:字段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 导出excel
// ** 需要在传入的结构体中的字段加上tag:excelize:"title:列头名称;index:列下标(从0开始);"
// list 需要导出的对象数组、sheet sheet名称、title 标题、isGhbj 是否设置隔行背景色
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
}

// 构造表头(endColName 最后一列的列名 dataRow 数据行开始的行号)
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() // 获取导入目标对象的类型信息
	var exportTitle []ExcelTag          // 遍历目标对象的字段
	for i := 0; i < dataType.NumField(); i++ {
		var excelTag ExcelTag
		field := dataType.Field(i) // 获取字段信息和tag
		tag := field.Tag.Get(ExcelTagKey)
		if tag == "" { // 如果非导出则跳过
			continue
		}
		if fields != "" { // 选择要导出或要忽略的字段
			if isIgnore && strings.Contains(fields, field.Name+",") { // 忽略指定字段
				continue
			}
			if !isIgnore && !strings.Contains(fields, field.Name+",") { // 导出指定字段
				continue
			}
		}
		err = excelTag.GetTag(tag)
		if err != nil {
			return
		}
		// 更改指定字段的表头标题
		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 { // 根据给定的宽度设置列宽
			_ = e.F.SetColWidth(sheet, endColName, endColName, float64(colTitle.Width))
		} else {
			_ = e.F.SetColWidth(sheet, endColName, endColName, float64(20)) // 默认宽度为20
		}
		titleRowData = append(titleRowData, colTitle.Name)
	}
	endColName = GetExcelColumnName(len(titleRowData)) // 根据列数生成 Excel 列名
	if title != "" {
		dataRow = 3 // 如果有title,那么从第3行开始就是数据行,第1行是title,第2行是表头
		e.F.SetCellValue(sheet, "A1", title)
		e.F.MergeCell(sheet, "A1", endColName+"1") // 合并标题单元格
		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 // 如果没有title,那么从第2行开始就是数据行,第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
}

// 构造数据行
func normalBuildDataRow(e *Excel, sheet, endColName, fields string, row int, isGhbj, isIgnore bool, dataValue reflect.Value) (err error) {
	//实时写入数据
	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 // 记录这一行中,数据最多的单元格的值的长度
		//遍历结构体的所有字段
		for j := 0; j < num; j++ {
			dataField := typ.Field(j) //获取到struct标签,需要通过reflect.Type来获取tag标签的值
			tagVal := dataField.Tag.Get(ExcelTagKey)
			if tagVal == "" { // 如果非导出则跳过
				continue
			}
			if fields != "" { // 选择要导出或要忽略的字段
				if isIgnore && strings.Contains(fields, dataField.Name+",") { // 忽略指定字段
					continue
				}
				if !isIgnore && !strings.Contains(fields, dataField.Name+",") { // 导出指定字段
					continue
				}
			}
			var dataCol ExcelTag
			err = dataCol.GetTag(tagVal)
			fieldData := item.FieldByName(dataField.Name) // 取字段值
			if fieldData.Type().String() == "string" {    // string类型的才计算长度
				rwsTemp := fieldData.Len() // 当前单元格内容的长度
				if rwsTemp > maxLen {      //这里取每一行中的每一列字符长度最大的那一列的字符
					maxLen = rwsTemp
				}
			}
			// 替换
			if dataCol.Replace != "" {
				split := strings.Split(dataCol.Replace, ",")
				for j := range split {
					s := strings.Split(split[j], "_") // 根据下划线进行分割,格式:需要替换的内容_替换后的内容
					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{} // 数据列
		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 { // 自适应行高
			d := maxLen / 25
			f := 25 * d
			_ = e.F.SetRowHeight(sheet, row, float64(f))
		} else {
			_ = e.F.SetRowHeight(sheet, row, float64(25)) // 默认行高25
		}
		if err = e.F.SetSheetRow(sheet, startCol, &rowData); err != nil {
			return
		}
		row++
	}
	return
}


// 下载
func DownLoadExcel(fileName string, res http.ResponseWriter, file *excelize.File) {
	// 设置响应头
	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文件内容到响应体
	if err != nil {
		http.Error(res, err.Error(), http.StatusInternalServerError)
		return
	}
}

测试

ok,终于写完了导入导出,接下来就是测试啦

go 导出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:用户账号;"`
	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_超级管理员,2_普通用户;"`
	Remark   string `excel:"name:备注;width:40;"`
}

// 导出
func export() {
	var testList = []Test{
		{"fuhua", "符华", "fuhua@123.com", "太虚剑派", "开发部", "CJGLY", "1", "备注备注"},
		{"baiye", "白夜", "baiye@123.com", "天命科技有限公司", "执行部", "PTYG", "2", ""},
		{"chiling", "炽翎", "chiling@123.com", "太虚剑派", "行政部", "PTYG", "2", "备注备注备注备注"},
		{"yunmo", "云墨", "yunmo@123.com", "太虚剑派", "财务部", "CJGLY", "1", ""},
		{"yuelun", "月轮", "yuelun@123.com", "天命科技有限公司", "执行部", "CJGLY", "1", ""},
		{"xunyu", "迅羽",
			"xunyu@123.com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这11111111111里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试",
			"天命科技有限公司", "开发部", "PTYG", "2",
			"备注备注备注备注com哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测里是最大行高测试哈哈哈哈哈哈哈哈这里是最大行高测试"},
	}
	changeHead := map[string]string{"Id": "账号", "Name": "真实姓名"}
	//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\\测试.xlsx"
	if err := f.Save(); err != nil {
		fmt.Println(err)
		return
	}
}

// 导入
func imports() {
	f, err := excelize.OpenFile("C:\\Users\\Administrator\\Desktop\\测试.xlsx")
	if err != nil {
		fmt.Println("文件打开失败")
	}
	importList := []Test{}
	err = excel.ImportExcel(f, &importList, 1, 2)
	if err != nil {
		fmt.Println(err)
	}
	for _, t := range importList {
		fmt.Println(t)
	}
}

实现效果

然后我们再来看看实现效果,说实话,我觉得这表格还挺好看的哩,不愧是我

go 导出excel,Go,excelize,golang,excel

导出

go 导出excel,Go,excelize,golang,excel

导入

go 导出excel,Go,excelize,golang,excel

最后

这样,我们就实现了一个通用的导入导出工具封装。

上面这些就是全部代码啦,后续等我把剩下几个复杂导出弄完(挖坑…),我会把这些代码全部抽出来,做成一个独立的组件模块,然后上传到Git上,这样以后不管做哪个项目,用的时候直接在go.mod引入就可以啦~完美😁
go 导出excel,Go,excelize,golang,excel

第二篇已更新:【Go】excelize库实现excel导入导出封装(二),基于map、多个sheet、多级表头、树形结构表头导出,横向、纵向合并单元格导出

好啦,以上就是本篇文章的全部内容了,如果你觉得对你有帮助或者觉得博主写得不错,千万不要吝啬你的大拇指哟(^U^)ノ~YO

go 导出excel,Go,excelize,golang,excel文章来源地址https://www.toymoban.com/news/detail-757580.html

到了这里,关于【Go】excelize库实现excel导入导出封装(一),自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端vue导出excel(标题加粗+表头自定义样式+表格边框+单元格自定义样式)

    接近过年,被一大堆excel报表烦死的我,遇到要求前端导出excel的后端,差点猝死的我拼命学习中,整理出这篇文章,希望看到这篇文章的你有所收获,也希望能收到大佬们的指点 之前用c#,.net弄过导出word,excel,可以点击查看.NET使用Aspose控件生成Word(可构建自定义表格)、

    2024年04月15日
    浏览(39)
  • SpringBoot实现Excel导入导出

    话不多说,直接上代码 依赖文档 找到pom文件,如下图所示 引入需要的依赖 导出写法: 导入写法: 已上就是导入导出

    2024年02月09日
    浏览(33)
  • POI 实现Excel导入导出

    什么是POI Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。 生成xls和xlsx有什么区别呢? XLS XLSX 只能打开xls格式,无法直接打开x

    2024年02月03日
    浏览(29)
  • JS实现Excel导入以及table导出为Excel

    在开发项目的过程中遇到了一个需求,将excel文件导入并且解析渲染到页面上。用户可以对表格内的部分内容做修改后再上传到服务端。 导入Excel 1.使用html支持上传标签从本地获取文件,例如type为file的input,el-upload等 2.实例化FileReader,并且通过readAsBinaryString将文件读取为二

    2024年02月09日
    浏览(27)
  • 纯前端实现 导入 与 导出 Excel

    最近经常在做 不规则 Excel 的导入,或者一些普通 Excel 的导出,当前以上说的都是纯前端来实现;下面我们来聊聊经常用到的Excel导出与导入的实现方案,本文实现技术栈以 Vue2 + JS 为例 导入分类: 调用 API 完全由后端来解析数据,清洗数据,前端只负责调用 API ; 前端解析

    2024年02月09日
    浏览(33)
  • 使用luckysheet实现excel导入导出

    luckysheet-demo: luckysheet-demoexcel导入导出实例 使用组件 1. luckysheet在线excel 2. luckyexcel excel导入插件 3. exceljs 导出excel数据 导出exceljs的二次封装,可直接使用如下

    2024年02月12日
    浏览(32)
  • 使用EasyPoi实现Excel的按模板样式导出

    1690342020350导出测试.xlsx 如下 #fe 使用#fe命令可以实现集合数据的横向拓展,比如模板代码是 导出的excel里面就会显示会自当前列,向右拓展,效果可参见下面的导出文件截图 v_fe 使用v_fe命令可以实现不固定列的横向遍历,比如模板代码是 分数 ID {{#fe:maths t.score t.id}} 这种情况

    2024年02月15日
    浏览(38)
  • SpringBoot实现Excel导入导出,简单好用

    POI是Java操作MicroOffice(如对Excel的导入导出)的一个插件。POI的全称是(Poor Obfuscation Implementation),POI官网地址是 http://poi.achache.org/index.html 。 EasyPoi对POI进行了优化 ,更加设计精巧,使用简单,接口丰富,扩展简单。EasyPOI的同类产品有Execel4J,Hutools等。EasyPoi官网地址是 h

    2024年02月11日
    浏览(33)
  • Vue中如何实现Excel导入导出

    在前面员工的添加是一个一个进行的,如果一次性添加多个员工信息,这时候就会很繁琐 因此需要我们开发一个批量导入的功能,将用户的信息存储到 excel 中然后进行批量导入 1. 分析 vue-element-admin 中的导入方案 在vue-element-admin中,大佬已经封装好了Excel的导入导出方案,我

    2023年04月14日
    浏览(33)
  • EasyExcel实现Excel文件导入导出功能

    Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。 EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。

    2024年02月02日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包