1 Star 0 Fork 0

袁中义/easyExcel

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
easyExcel.go 7.96 KB
一键复制 编辑 原始数据 按行查看 历史
袁中义 提交于 2025-12-16 14:54 +08:00 . 修复合并列为空的bug
package easyExcel
import (
"archive/zip"
"encoding/xml"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"gitee.com/netbycom/easyExcel/common"
"gitee.com/netbycom/easyExcel/model"
"gitee.com/netbycom/easyExcel/util"
"github.com/wxnacy/wgo/file"
)
type EasyExcel struct {
outputDir string
}
func (e *EasyExcel) GetSheetList() ([]*model.WorkSheet, error) {
workbookPath := path.Join(e.outputDir, common.WORKBOOK_PATH)
xmlFile, err := os.OpenFile(workbookPath, os.O_RDONLY, os.ModePerm)
if err != nil {
return nil, fmt.Errorf("failed to open workbook at %q: %w", workbookPath, err)
}
defer xmlFile.Close()
var workbook = new(model.Workbook)
err = xml.NewDecoder(xmlFile).Decode(&workbook)
if err != nil {
return nil, fmt.Errorf("failed to parse workbook at %q: %w", workbookPath, err)
}
var sheets []*model.WorkSheet
for _, item := range workbook.Sheets.Sheets {
sheets = append(sheets, &model.WorkSheet{
SheetId: fmt.Sprintf("sheet%d", item.SheetId),
Name: item.Name,
})
}
return sheets, nil
}
func (e *EasyExcel) GetRows(sheet string) ([]*model.SheetRow, error) {
sharedStrings, err := e.readSharedStrings(sheet)
if err != nil {
return nil, fmt.Errorf("failed to parse worksheet at %q: %w", sheet, err)
}
worksheet, err := e.parseWorksheet(sheet)
if err != nil {
return nil, fmt.Errorf("failed to parse worksheet at %q: %w", sheet, err)
}
mapMergeCell := e.processMergeCells(worksheet)
var rows = make([]*model.SheetRow, 0)
for rowIndex, item := range worksheet.SheetData.Rows {
mergeCells := ""
if mergeCell, ok := mapMergeCell[rowIndex+1]; ok {
mergeCells = mergeCell
}
row := model.SheetRow{
Index: rowIndex + 1,
Cols: make([]*model.Col, 0),
MergeCells: mergeCells,
}
for cellIndex, cell := range item.Cells {
value := ""
if cell.Type == "s" {
index, _ := strconv.Atoi(cell.Value)
value = sharedStrings[index]
} else {
value = cell.Value
}
col := &model.Col{
Index: cellIndex + 1,
Cell: cell.Ref,
Value: fmt.Sprintf("%v", value),
}
row.Cols = append(row.Cols, col)
}
rows = append(rows, &row)
}
return rows, nil
}
func (e *EasyExcel) GetPictures(sheet string) (map[string]*model.Picture, error) {
drawing, err := e.parseDrawing(sheet)
if err != nil {
return nil, fmt.Errorf("failed to parse drawings at %q: %w", sheet, err)
}
mapRelationShip, err := e.parseRelationship(sheet)
if err != nil {
return nil, fmt.Errorf("failed to parse relationships at %q: %w", sheet, err)
}
var mapPicture = make(map[string]*model.Picture)
for _, item := range drawing.TwoCellAnchors {
filename := ""
if relationship, ok := mapRelationShip[item.Pic.BlipFill.Blip.Embed]; ok {
filename = path.Base(relationship.Target)
}
fromCol, _ := strconv.Atoi(item.From.Col)
fromRow, _ := strconv.Atoi(item.From.Row)
toCol, _ := strconv.Atoi(item.To.Col)
toRow, _ := strconv.Atoi(item.To.Row)
from := util.RowCell{
Col: fromCol,
ColOff: 0,
Row: fromRow,
RowOff: 0,
}
to := util.RowCell{
Col: toCol,
ColOff: 0,
Row: toRow,
RowOff: 0,
}
cell := util.GetCellFromRowAndCell(&from, &to)
picFile, err := os.Open(path.Join(e.outputDir, common.MEDIA_PATH, filename))
if err != nil {
return nil, err
}
defer picFile.Close()
bytes, err := io.ReadAll(picFile)
if err != nil {
return nil, err
}
mapPicture[cell] = &model.Picture{
Filename: filename,
Extension: filepath.Ext(filename),
File: bytes,
}
}
return mapPicture, nil
}
func (e *EasyExcel) readSharedStrings(sheet string) (map[int]string, error) {
sharedFile, err := os.Open(path.Join(e.outputDir, common.SHAREDSTRINGS))
if err != nil {
return nil, fmt.Errorf("failed to open worksheet at %q: %w", sheet, err)
}
defer sharedFile.Close()
content, err := io.ReadAll(sharedFile)
if err != nil {
return nil, err
}
var sst model.SST
err = xml.Unmarshal(content, &sst)
if err != nil {
return nil, fmt.Errorf("failed to parse worksheet at %q: %w", sheet, err)
}
var sharedStrings = make(map[int]string)
for index, si := range sst.SI {
sharedStrings[index] = si.GetText()
}
return sharedStrings, nil
}
func (e *EasyExcel) parseWorksheet(sheet string) (*model.Worksheet, error) {
xmlFile, err := os.Open(path.Join(e.outputDir, common.WORKSHEETS_PATH, sheet+".xml"))
if err != nil {
return nil, fmt.Errorf("failed to open worksheet at %q: %w", sheet, err)
}
defer xmlFile.Close()
var worksheet = new(model.Worksheet)
err = xml.NewDecoder(xmlFile).Decode(&worksheet)
if err != nil {
return nil, fmt.Errorf("failed to parse worksheet at %q: %w", sheet, err)
}
return worksheet, nil
}
func (e *EasyExcel) processMergeCells(worksheet *model.Worksheet) map[int]string {
var mapMergeCell = make(map[int]string)
if worksheet.MergeCells != nil && len(worksheet.MergeCells.MergeCells) > 0 {
for _, mergeCell := range worksheet.MergeCells.MergeCells {
mapCell := util.GetMergeCellRowIndex(mergeCell.Ref)
for index, cell := range mapCell {
mapMergeCell[index] = cell
}
}
}
return mapMergeCell
}
func (e *EasyExcel) parseDrawing(sheet string) (*model.RDrawing, error) {
sheetId := strings.ReplaceAll(sheet, "sheet", "")
xmlFile, err := os.Open(path.Join(e.outputDir, common.DRAWINGS_PATH, fmt.Sprintf("drawing%s.xml", sheetId)))
if err != nil {
return nil, fmt.Errorf("failed to open drawings at %q: %w", sheet, err)
}
defer xmlFile.Close()
var drawing = new(model.RDrawing)
err = xml.NewDecoder(xmlFile).Decode(drawing)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal drawing: %s", err)
}
return drawing, nil
}
func (e *EasyExcel) parseRelationship(sheet string) (map[string]*model.Relationship, error) {
sheetId := strings.ReplaceAll(sheet, "sheet", "")
relFile, err := os.Open(path.Join(e.outputDir, common.DRAWINGS_PATH, "_rels", fmt.Sprintf("drawing%s.xml.rels", sheetId)))
if err != nil {
return nil, fmt.Errorf("failed to open drawings at %q: %w", sheet, err)
}
defer relFile.Close()
var relationShips = new(model.Relationships)
err = xml.NewDecoder(relFile).Decode(relationShips)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal relationships: %s", err)
}
var mapRelationShip = make(map[string]*model.Relationship)
if len(relationShips.Relationships) > 0 {
for _, item := range relationShips.Relationships {
mapRelationShip[item.Id] = item
}
}
return mapRelationShip, nil
}
func (e *EasyExcel) Close() error {
err := os.RemoveAll(e.outputDir)
if err != nil {
return fmt.Errorf("failed to remove output directory %s: %v", e.outputDir, err)
}
return nil
}
func OpenFile(filename string) (*EasyExcel, error) {
if !file.Exists(filename) {
return nil, fmt.Errorf("file %s does not exist", filename)
}
if path.Ext(filename) != ".xlsx" {
return nil, fmt.Errorf("file %s is not .xlsx", filename)
}
// 创建一个目录来存储解压后的文件
outputDir, err := os.MkdirTemp("", "xlsx_extract_*")
if err != nil {
return nil, fmt.Errorf("failed to create directory %s: %v", outputDir, err)
}
err = util.ReadZipReader(filename, outputDir)
if err != nil {
return nil, err
}
easyExcel := &EasyExcel{
outputDir: outputDir,
}
return easyExcel, nil
}
func NewExcelWriter(filename string) (*model.ExcelWriter, error) {
excelFile, err := os.Create(filename)
if err != nil {
return nil, fmt.Errorf("failed to create excel file %q: %v", filename, err)
}
zipWriter := zip.NewWriter(excelFile)
return &model.ExcelWriter{
File: excelFile,
Writer: zipWriter,
WorkSheets: make([]*model.Worksheet, 0),
SharedStrings: &model.SharedString{
Index: make(map[string]int),
Values: make([]string, 0),
Count: 0,
},
StyleSheet: model.NewDefaultCellStyle(),
StyleSheetMap: make(map[*model.Style]bool),
DrawingMap: make(map[int]*model.Drawing),
DrawingRelationshipMap: make(map[int]*model.Relationships),
SheetRelationshipMap: make(map[int]*model.Relationships),
AllImages: make(map[string]int),
}, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/netbycom/easyExcel.git
git@gitee.com:netbycom/easyExcel.git
netbycom
easyExcel
easyExcel
v1.2.4

搜索帮助