add import
parent
c4fa0fad1e
commit
e9d6617baa
|
@ -1,6 +1,6 @@
|
|||
# xlsx
|
||||
|
||||
excel的导出导入(目前仅完成导出)
|
||||
excel的导出和导入
|
||||
|
||||
## 导出功能
|
||||
如果要使用导出,则传入的数据必须为指针,类型可以是struct或者元素为struct的slice。
|
||||
|
@ -9,3 +9,10 @@ excel的导出导入(目前仅完成导出)
|
|||
当导出的excel表格列数不确定时,tag使用`export:"loop:true"`,这样会将所有字段导出到excel表格中。
|
||||
|
||||
详情请看测试文件
|
||||
|
||||
## 导入功能
|
||||
如果要使用导入,则传入的数据必须为指针,类型是元素为struct的slice。
|
||||
|
||||
通过tag来确认需要导入的字段,内容为`import:"index:1"`,index为excel表格中的列数,从1开始。当需要导入的数据为日期时,结构体中必须为time.Time类型,不可以用自定义的日期类型,且excel表格中的日期格式必须为yyyy-mm-dd或yyyy-mm-dd hh:mm:ss。
|
||||
|
||||
详细请看测试文件
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package xlsx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.botann.com/lijun/xlsx/imports"
|
||||
)
|
||||
|
||||
type Data struct {
|
||||
Autoseq int
|
||||
Name string `import:"true,index:1"`
|
||||
Unit string `import:"true,index:2"`
|
||||
TestCol string
|
||||
Invest int64 `import:"true,index:3"`
|
||||
CreateTime time.Time `import:"true,index:4"`
|
||||
}
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
var data []Data
|
||||
importer, err := imports.NewFileImporter("./test7.xlsx", &data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
err = importer.Import(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(data)
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package imports
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type Importer struct {
|
||||
Reader io.Reader
|
||||
Sheets []string
|
||||
xlsxFile *excelize.File
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func NewFileImporter(file string, data any) (*Importer, error) {
|
||||
f, err := excelize.OpenFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sheets := f.GetSheetList()
|
||||
return &Importer{
|
||||
xlsxFile: f,
|
||||
Sheets: sheets,
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewFileReaderImporter(reader io.Reader, data any) (*Importer, error) {
|
||||
f, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sheets := f.GetSheetList()
|
||||
return &Importer{
|
||||
xlsxFile: f,
|
||||
Sheets: sheets,
|
||||
Data: data,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *Importer) Import(sheetIndex int) error {
|
||||
v := reflect.ValueOf(i.Data)
|
||||
if v.Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("data must be pointer")
|
||||
}
|
||||
switch v.Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
return i.dealFile(sheetIndex)
|
||||
default:
|
||||
return fmt.Errorf("data must be slice")
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Importer) dealFile(sheetIndex int) error {
|
||||
sheet := i.Sheets[sheetIndex]
|
||||
rows, err := i.xlsxFile.Rows(sheet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vdata := reflect.ValueOf(i.Data).Elem()
|
||||
if vdata.Type().Elem().Kind() != reflect.Struct {
|
||||
return fmt.Errorf("element must be struct")
|
||||
}
|
||||
testData := reflect.New(vdata.Type().Elem())
|
||||
tempDic := make(map[int]string)
|
||||
for i := 0; i < testData.Elem().NumField(); i++ {
|
||||
t := testData.Elem().Type().Field(i)
|
||||
tag, ok := t.Tag.Lookup("import")
|
||||
if ok {
|
||||
tagMap := dealTag(tag)
|
||||
index, _ := strconv.Atoi(tagMap["index"])
|
||||
tempDic[index] = t.Name
|
||||
}
|
||||
}
|
||||
rowIndex := 0
|
||||
for rows.Next() {
|
||||
rowIndex += 1
|
||||
// 第一行为表头
|
||||
if rowIndex > 1 {
|
||||
row, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newData := reflect.New(vdata.Type().Elem())
|
||||
for colIndex := range row {
|
||||
colIndex += 1
|
||||
cellName, _ := excelize.CoordinatesToCellName(colIndex, rowIndex)
|
||||
value, _ := i.xlsxFile.GetCellValue(sheet, cellName)
|
||||
if fieldName, ok := tempDic[colIndex]; ok {
|
||||
field := newData.Elem().FieldByName(fieldName)
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
field.SetString(value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
intValue, _ := strconv.Atoi(value)
|
||||
field.SetInt(int64(intValue))
|
||||
case reflect.Float32, reflect.Float64:
|
||||
floatValue, _ := strconv.ParseFloat(value, 64)
|
||||
field.SetFloat(floatValue)
|
||||
case reflect.Bool:
|
||||
boolValue, _ := strconv.ParseBool(value)
|
||||
field.SetBool(boolValue)
|
||||
// 如果是结构体,只支持时间类型,并且结构体的字段类型必须是time.Time,不能为自定的时间类型,并且excel里面的时间格式必须是yyyy-mm-dd或者yyyy-mm-dd hh:mm:ss
|
||||
case reflect.Struct:
|
||||
t := time.Time{}
|
||||
if strings.Contains(value, ":") {
|
||||
t, _ = time.Parse(time.DateTime, value)
|
||||
} else {
|
||||
t, _ = time.Parse(time.DateOnly, value)
|
||||
}
|
||||
field.Set(reflect.ValueOf(t))
|
||||
}
|
||||
}
|
||||
}
|
||||
vdata.Set(reflect.Append(vdata, newData.Elem()))
|
||||
}
|
||||
}
|
||||
if err = rows.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dealTag(tag string) map[string]string {
|
||||
tag_list := strings.Split(tag, ",")
|
||||
tag_map := make(map[string]string)
|
||||
for _, tag := range tag_list {
|
||||
tag_kv := strings.Split(tag, ":")
|
||||
if len(tag_kv) != 2 {
|
||||
continue
|
||||
}
|
||||
tag_map[tag_kv[0]] = tag_kv[1]
|
||||
}
|
||||
return tag_map
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue