golang 利用反射写excel

excel操作接口封装

package excel

import (
	"context"
	"fmt"
	"reflect"
	"strings"

	"github.com/360EntSecGroup-Skylar/excelize"
)

type Field struct {
	Value interface{}
}

//excel操作接口
type IExcelHandler interface {
	// 获取处理器标识名,用途有:
	// 1.根据标识名替换某个处理器
	// 2.打印日志
	GetName() string

	//获取sheet name
	//打印日志
	GetSheetName() string

	// 获取需要写excel的数据
	GetField() *Field

	//获取源路径
	GetSourcePath() string

	//获取目标路径
	GetTargetPath() string

	//设置源路径
	SetSourcePath(path string) error

	//设置导出excel路径
	SetTargetPath(path string) error

	Write(ctx context.Context, xlsx *excelize.File, data interface{}) error

	OpenFile(ctx context.Context, path string) (*excelize.File, error)

	SaveAs(ctx context.Context, xlsx *excelize.File, path string) error
}

type WriteExcelHandle struct {
	HandlerName string //处理器标识名
	SheetName   string //excel表名
	StartIndex  int    //从第几行开始插入数据
	SourcePath  string //excel文件原始路径,比如,模板位置
	TargetPath  string //excel文件目标路径,比如,另存为路径
	IsSetBorder bool   //是否需要设置边框
	Data        *Field
}

func (w *WriteExcelHandle) GetName() string {
	if w == nil {
		return ""
	}
	return w.HandlerName
}

func (w *WriteExcelHandle) GetSheetName() string {
	if w == nil {
		return ""
	}
	return w.SheetName
}

func (w *WriteExcelHandle) GetField() *Field {
	if w == nil {
		return &Field{}
	}
	return w.Data
}

func (w *WriteExcelHandle) GetSourcePath() string {
	if w == nil {
		return ""
	}
	return w.SourcePath
}

func (w *WriteExcelHandle) GetTargetPath() string {
	if w == nil {
		return ""
	}
	return w.TargetPath
}

func (w *WriteExcelHandle) SetSourcePath(path string) error {
	if w == nil {
		return fmt.Errorf("WriteExcelHandle can not be nil")
	}
	w.SourcePath = path
	return nil
}

func (w *WriteExcelHandle) SetTargetPath(path string) error {
	if w == nil {
		return fmt.Errorf("WriteExcelHandle can not be nil")
	}
	w.TargetPath = path
	return nil
}

//打开文件
func (w *WriteExcelHandle) OpenFile(ctx context.Context, path string) (*excelize.File, error) {
	//打开excel文件
	xlsx, err := excelize.OpenFile(path)
	if err != nil {
		return nil, fmt.Errorf("open file failed:%s", err)
	}
	return xlsx, nil
}

//excel另存为
func (w *WriteExcelHandle) SaveAs(ctx context.Context, xlsx *excelize.File, path string) error {
	err := xlsx.SaveAs(path)
	if err != nil {
		return err
	}
	return nil
}

//利用反射的特点写excel,根据tag获取列名
//其中tag命名规则如下:xlsx:"列index"或者xlsx:"列index-行index"
//如果指定了行index,则写入指定的cell中,否则行index需要根据startIndex动态生成
func (w *WriteExcelHandle) Write(ctx context.Context, xlsx *excelize.File, data interface{}) error {
	typ := reflect.TypeOf(data)
	val := reflect.Indirect(reflect.ValueOf(data))
	if typ.Kind() == reflect.Slice {
		for i := 0; i < val.Len(); i++ {
			//不强转的话,直接对这个新的interface{}再做一次反射
			r := val.Index(i).Interface()
			d := reflect.TypeOf(r).Elem()
			for j := 0; j < d.NumField(); j++ {
				xlsxTag := d.Field(j).Tag.Get("xlsx")
				arrayTag := strings.Split(xlsxTag, "-")
				var column string
				var cellIndex string
				if len(arrayTag) <= 0 {
					return fmt.Errorf("invalid xlsx tag")
				}
				if len(arrayTag) >= 1 {
					column = arrayTag[0]
				}
				//拼接单元格索引
				cellIndex = fmt.Sprintf("%s%d", column, i+w.StartIndex)
				if len(arrayTag) == 2 {
					rowIndex := arrayTag[1]
					if rowIndex == "" {
						return fmt.Errorf("invalid xlsx tag")
					}
					cellIndex = fmt.Sprintf("%s%s", column, rowIndex)
				}
				// 设置内容
				switch d.Field(j).Type.String() {
				//目前只有string类型,有其他类型再进行扩展
				case "string":
					fieldVal := reflect.ValueOf(r).Elem().Field(j).String()
					xlsx.SetCellValue(w.SheetName, cellIndex, fieldVal)
				}
				//设置边框
				if w.IsSetBorder {
					SetBorder(xlsx, w.SheetName, cellIndex, cellIndex)
				}
			}
			// 测试环境编译的版本用的是go1.12.9,而复制黏贴单元格功能需要go1.15及以上
			// //复制黏贴行,最后一次不复制
			// if i < val.Len()-1 {
			// 	xlsx.DuplicateRowTo(w.SheetName, i+w.StartIndex, i+w.StartIndex+1)
			// }
		}
	}
	return nil
}

//设置边框
func SetBorder(xlsx *excelize.File, sheetName string, hcell, vcell string) error {
	style, err := xlsx.NewStyle(`{"border":[{"type":"bottom","color":"000000","style":1},{"type":"right","color":"000000","style":1}]}`)
	if err != nil {
		return err
	}
	xlsx.SetCellStyle(sheetName, hcell, vcell, style)
	return nil
}

事例:

package main

import (
	"context"
	"fmt"
	"reflect"
	"strings"
	"time"
    //需要引入封装好的excel包
)

type Record struct {
	Name string `xlsx:"A-姓名"`
	Age  int32  `xlsx:"B-年齡"`
}

func main() {
var records []*Record
	records = append(records, &Record{
		Name: "小明",
		Age:  11,
	})
	records = append(records, &Record{
		Name: "小华",
		Age:  12,
	})
//数据写Excel
	sourcePath := "excel第一次打开的路径"
	targetPath := "excel保存路径"
	excelHandlers := make([]excel.IExcelHandler, 0)
//sheet1
	var ISheet1Handler excel.IExcelHandler
	ISheet1Handler = &excel.WriteExcelHandle{
		HandlerName: "write sheet1 handler",
		SheetName:   "sheet1",
		StartIndex:  2, //这个值依赖于创建的excel模板
		Data:        &excel.Field{Value: records},
		IsSetBorder: true,
	}
	excelHandlers = append(excelHandlers, ISheet1Handler)
	//sheet2
	var ISheet2Handler excel.IExcelHandler
	ISheet2Handler = &excel.WriteExcelHandle{
		HandlerName: "write sheet2 handler",
		SheetName:   "sheet2",
		StartIndex:  2,
		TargetPath:  targetPath,
		Data:        &excel.Field{Value: sellGoodsOverviewHandle.Output},
	}
	excelHandlers = append(excelHandlers, ISheet2Handler)

	writeExcel(context.Background(), excelHandlers...)
}

func writeExcel(ctx context.Context, handler ...excel.IExcelHandler) (string, error) {
	var err error
	var path string
	xlsx := new(excelize.File)
	for index, h := range handler {
		//首先打开文件
		if index == 0 {
			xlsx, err = h.OpenFile(ctx, h.GetSourcePath())
			if err != nil {
				uclog.Error("open file error:%s", err.Error())
			}
		}
		err = h.Write(ctx, xlsx, h.GetField().Value)
		if err != nil {
			uclog.Error(h.GetName()+"error:%s", err.Error())
			return "", fmt.Errorf(h.GetName()+"error:%s", err.Error())
		}
		//数据写结束,保存excel
		if index == len(handler)-1 {
			err = h.SaveAs(ctx, xlsx, h.GetTargetPath())
			if err != nil {
				uclog.Error("save file error:%s", err.Error())
			}
			path = h.GetTargetPath()
		}
		uclog.Info("%s to %s sucess", h.GetName(), h.GetSheetName())
	}
	return path, nil
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值