Contents

使用golang导出excel

原因

在日常开发中,经常会遇到要导出数据excel的需求,而go 的强类型就导致数据转换不是很方便。下面记录下我自己最近做的一个小需求,导出数据库某个model所有数据。使用golang 的反射和excel处理模块excelize

数据定义

type ExportData struct {
	ID          string `json:"id" title:"序号" column:"A"`
	Ip          string `json:"ip" title:"IP" column:"B"`
	BlockTime   string `json:"block_time" title:"封禁时间" column:"C"`
	BlockMode   string `json:"block_mode" title:"封禁方式" column:"D"`
	UnblockTime string `json:"unblock_time,omitempty"  title:"解封时间" column:"E"`
	UnblockMode string `json:"unblock_mode,omitempty" title:"解封方式" column:"F"`
	Fw          string `json:"fw" title:"防火墙" column:"G"`
}

这里我们为每一个字段加上的tag 加上自定义的tag,title 代表表头的字段,column 代表在excel 的那一列,后面可以用反射取出来拼接数据

生成表头和设置格式

const (
    CurrentBlock = "当前封禁"
    HistoryBlock = "历史记录"
)
func initFile() (*excelize.File, error) {
	var export ExportData
	types := reflect.TypeOf(export)

	f := excelize.NewFile()
	f.SetSheetName("Sheet1", CurrentBlock)
	current := f.GetSheetIndex(CurrentBlock)
	history := f.NewSheet(HistoryBlock)
	for i := 0; i < types.NumField(); i++ {
		title := types.Field(i).Tag.Get("title")
		column := types.Field(i).Tag.Get("column")
		var headerRow = 1

		strId := strconv.FormatInt(int64(headerRow), 10)
		if i < 4 {
			if err := f.SetCellValue(CurrentBlock, column+strId, title); err != nil {
				return nil, err
			}
		}
		if err := f.SetCellStr(HistoryBlock, column+strId, title); err != nil {
			return nil, err
		}
	}
	f.SetActiveSheet(history)
	f.SetActiveSheet(current)
	return f, nil
}

根据定义的title 生成表头,并且通过column指定写入的行数

写入数据

这里因为业务需求,有两个sheet,字段有区别 所以要处理写入的column

func whiteXlsxFile(record []ExportData, unblock bool, file *excelize.File) (*excelize.File, error) {
	types := reflect.TypeOf(ExportData{})
	fieldLength := types.NumField()
	for i, row := range record {
		values := reflect.ValueOf(row)
		// 使用不同的sheet
		var sheet = CurrentBlock
		if unblock {
			sheet = HistoryBlock
		}
		for j := 0; j < fieldLength; j++ {
			column := types.Field(j).Tag.Get("column")
			val := parseBlockMode(values.Field(j).String())
			if unblock {
				// 当前解禁只写到D列
				if column == "E" && !unblock {
					break
				}
			}
			idCell := fmt.Sprintf("A%d", i+2) // i+2 跳过表头
			cell := fmt.Sprintf("%s%d", column, i+2)
			if err := file.SetCellStr(sheet, idCell, fmt.Sprintf("%d", i+1)); err != nil {
				return nil, err
			}
			if err := file.SetCellStr(sheet, cell, val); err != nil {
				return nil, err
			}
		}
	}
	return file, nil
}

写入响应

writer :=http.ResponseWriter // 换成自己的writer
writer.Header().Set("Content-Disposition", `attachment; filename="文件名称.xlsx";`)
writer.WriteHeader(200)

file, err := utils.GenXlsxFile(res, historyRecord)
if err != nil {
    return err
}
if _, err := file.WriteTo(writer); err != nil {
    return err
}