1

简单的excel导入导出

 1 year ago
source link: https://zsmhub.github.io/post/golang/%E7%AE%80%E5%8D%95%E7%9A%84excel%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

excel导入导出代码

package tool

import (
    "errors"
    "github.com/360EntSecGroup-Skylar/excelize/v2"
    "mime/multipart"
    "strconv"
)

const (
    maxCharCount     = 26       // maxCharCount 最多26个字符A-Z
    defaultSheetName = "Sheet1" // 默认Sheet名称
)

type Excel struct {
    SheetName string          // sheetName,默认为Sheet1
    Headers   []string        // 表头
    Rows      [][]interface{} // 表数据
}

func (m *Excel) SetDefaultSheetName() {
    if m.SheetName == "" {
        m.SheetName = defaultSheetName
    }
}

// 从文件流读取 Excel Sheet1 的所有数据
func (Excel) ReadExcelFromStream(file *multipart.FileHeader) ([][]string, error) {
    var rows [][]string

    // 默认最大上传 2 M
    maxFileSize := int64(2)

    src, err := file.Open()
    if err != nil {
        return rows, err
    }
    defer src.Close()

    // 兆字节转为字节进行比较大小
    if file.Size > maxFileSize*1e6 {
        return rows, errors.New("文件大小不能超过 " + strconv.FormatInt(maxFileSize, 10) + " M")
    }

    f, err := excelize.OpenReader(src)
    if err != nil {
        return rows, errors.New(err.Error())
    }

    // 获取 Sheet1 上所有单元格
    rows, err = f.GetRows(f.GetSheetName(0))
    if err != nil {
        return rows, errors.New(err.Error())
    }

    return rows, nil
}

// 导出excel到浏览器
func (m Excel) ExportToWeb() ([]byte, error) {
    f, err := m.exportExcel()
    if err != nil {
        return nil, err
    }
    buffer, err := f.WriteToBuffer()
    if err != nil {
        return nil, err
    }
    return buffer.Bytes(), nil
}

// 导出excel到本地服务器
// path 文件导出路径,建议使用绝对路径
// filename 文件名,需带上后缀
func (m Excel) ExportToPath(path, filename string) (string, error) {
    var filePath string
    f, err := m.exportExcel()
    if err != nil {
        return filePath, err
    }
    filePath = path + "/" + filename
    if err := f.SaveAs(filePath); err != nil {
        return filePath, err
    }
    return filePath, nil
}

// exportExcel 导出Excel文件
// sheetName 工作表名称
// headers 列名切片, 表头
// rows 数据切片,是一个二维数组
func (m Excel) exportExcel() (*excelize.File, error) {
    m.SetDefaultSheetName()

    f := excelize.NewFile()
    sheetIndex := f.NewSheet(m.SheetName)
    maxColumnRowNameLen := 1 + len(strconv.Itoa(len(m.Rows)))
    columnCount := len(m.Headers)
    if columnCount > maxCharCount {
        maxColumnRowNameLen++
    } else if columnCount > maxCharCount*maxCharCount {
        maxColumnRowNameLen += 2
    }
    columnNames := make([][]byte, 0, columnCount)
    for i, header := range m.Headers {
        columnName := m.getColumnName(i, maxColumnRowNameLen)
        columnNames = append(columnNames, columnName)
        // 初始化excel表头,这里的index从1开始要注意
        curColumnName := m.getColumnRowName(columnName, 1)
        err := f.SetCellValue(m.SheetName, curColumnName, header)
        if err != nil {
            return nil, err
        }
    }
    for rowIndex, row := range m.Rows {
        for columnIndex, columnName := range columnNames {
            // 从第二行开始
            err := f.SetCellValue(m.SheetName, m.getColumnRowName(columnName, rowIndex+2), row[columnIndex])
            if err != nil {
                return nil, err
            }
        }
    }
    f.SetActiveSheet(sheetIndex)
    return f, nil
}

// getColumnName 生成列名
// Excel的列名规则是从A-Z往后排;超过Z以后用两个字母表示,比如AA,AB,AC;两个字母不够以后用三个字母表示,比如AAA,AAB,AAC
// 这里做数字到列名的映射:0 -> A, 1 -> B, 2 -> C
// maxColumnRowNameLen 表示名称框的最大长度,假设数据是10行,1000列,则最后一个名称框是J1000(如果有表头,则是J1001),是4位
// 这里根据 maxColumnRowNameLen 生成切片,后面生成名称框的时候可以复用这个切片,而无需扩容
func (m Excel) getColumnName(column, maxColumnRowNameLen int) []byte {
    const A = 'A'
    if column < maxCharCount {
        // 第一次就分配好切片的容量
        slice := make([]byte, 0, maxColumnRowNameLen)
        return append(slice, byte(A+column))
    } else {
        // 递归生成类似AA,AB,AAA,AAB这种形式的列名
        return append(m.getColumnName(column/maxCharCount-1, maxColumnRowNameLen), byte(A+column%maxCharCount))
    }
}

// getColumnRowName 生成名称框
// Excel的名称框是用A1,A2,B1,B2来表示的,这里需要传入前一步生成的列名切片,然后直接加上行索引来生成名称框,就无需每次分配内存
func (Excel) getColumnRowName(columnName []byte, rowIndex int) (columnRowName string) {
    l := len(columnName)
    columnName = strconv.AppendInt(columnName, int64(rowIndex), 10)
    columnRowName = string(columnName)
    // 将列名恢复回去
    columnName = columnName[:l]
    return
}

WEB端调用示例

package controller

import (
    "bytes"
    "github.com/labstack/echo/v4"
    "xxx/lib/tool"
    "net/http"
)

type xxxController struct{}

var XxxController = new(xxxController)

func init() {
    Router.GET("/export", AuthCorpController.Export).Name = "导出excel"
}

func (xxxController) Export(c echo.Context) error {
    var (
        excelHeaders = []string{"姓名", "性别", "年龄"}
        excelRows    = [][]interface{}{
            {"张三", "女", 18},
            {"李四", "男", 19},
        }
    )
    b, _ := tool.Excel{Headers: excelHeaders, Rows: excelRows}.ExportToWeb()
    c.Response().Header().Set(echo.HeaderContentDisposition, "attachment; filename=students.xlsx")
    return c.Stream(http.StatusOK, echo.MIMEOctetStream, bytes.NewReader(b))
}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK