Browse Source

- Improved performance when reading large files, call Token to read tokens one by one instead Unmarshal. Related issue #20 ;
- Fix go test typo;
- Update README

Ri Xu 8 years ago
parent
commit
0833a9d5da
7 changed files with 94 additions and 28 deletions
  1. 6 1
      README.md
  2. 24 0
      cell.go
  3. 28 0
      excelize.go
  4. 1 1
      excelize_test.go
  5. 0 6
      lib.go
  6. 6 1
      picture.go
  7. 29 19
      rows.go

+ 6 - 1
README.md

@@ -110,13 +110,18 @@ func main() {
     }
     // Insert a picture.
     err = xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.gif", 0, 0, 1, 1)
+    if err != nil {
+        fmt.Println(err)
+    }
     // Insert a picture to sheet with scaling.
     err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image2.jpg", 0, 0, 0.5, 0.5)
+    if err != nil {
+        fmt.Println(err)
+    }
     // Insert a picture offset in the cell.
     err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.png", 15, 10, 1, 1)
     if err != nil {
         fmt.Println(err)
-        os.Exit(1)
     }
     // Save the xlsx file with the origin path.
     err = xlsx.Save()

+ 24 - 0
cell.go

@@ -106,6 +106,14 @@ func (f *File) SetCellFormula(sheet, axis, formula string) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
 	if xlsx.MergeCells != nil {
 		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
 			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@@ -143,6 +151,14 @@ func (f *File) SetCellHyperLink(sheet, axis, link string) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
 	if xlsx.MergeCells != nil {
 		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
 			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@@ -205,6 +221,14 @@ func (f *File) MergeCell(sheet, hcell, vcell string) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
 	if xlsx.MergeCells != nil {
 		mergeCell := xlsxMergeCell{}
 		// Correct the coordinate area, such correct C1:B3 to B1:C3.

+ 28 - 0
excelize.go

@@ -10,6 +10,7 @@ import (
 
 // File define a populated XLSX file struct.
 type File struct {
+	checked    map[string]bool
 	XLSX       map[string]string
 	Path       string
 	SheetCount int
@@ -21,6 +22,7 @@ func OpenFile(filename string) (*File, error) {
 	var f *zip.ReadCloser
 	var err error
 	file := make(map[string]string)
+	c := make(map[string]bool)
 	sheetCount := 0
 	f, err = zip.OpenReader(filename)
 	if err != nil {
@@ -28,6 +30,7 @@ func OpenFile(filename string) (*File, error) {
 	}
 	file, sheetCount, _ = ReadZip(f)
 	return &File{
+		checked:    c,
 		XLSX:       file,
 		Path:       filename,
 		SheetCount: sheetCount,
@@ -66,6 +69,15 @@ func (f *File) SetCellInt(sheet string, axis string, value int) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
+
 	if xlsx.MergeCells != nil {
 		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
 			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@@ -98,6 +110,14 @@ func (f *File) SetCellStr(sheet string, axis string, value string) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
 	if xlsx.MergeCells != nil {
 		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
 			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {
@@ -133,6 +153,14 @@ func (f *File) SetCellDefault(sheet string, axis string, value string) {
 	var xlsx xlsxWorksheet
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
 	xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	if f.checked == nil {
+		f.checked = make(map[string]bool)
+	}
+	ok := f.checked[name]
+	if !ok {
+		xlsx = checkRow(xlsx)
+		f.checked[name] = true
+	}
 	if xlsx.MergeCells != nil {
 		for i := 0; i < len(xlsx.MergeCells.Cells); i++ {
 			if checkCellInArea(axis, xlsx.MergeCells.Cells[i].Ref) {

+ 1 - 1
excelize_test.go

@@ -236,7 +236,7 @@ func TestSetSheetBackground(t *testing.T) {
 	}
 }
 
-func TestSMergeCell(t *testing.T) {
+func TestMergeCell(t *testing.T) {
 	xlsx, err := OpenFile("./test/Workbook1.xlsx")
 	if err != nil {
 		t.Log(err)

+ 0 - 6
lib.go

@@ -3,7 +3,6 @@ package excelize
 import (
 	"archive/zip"
 	"bytes"
-	"encoding/xml"
 	"io"
 	"log"
 	"math"
@@ -26,11 +25,6 @@ func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
 		fileList[v.Name] = readFile(v)
 		if len(v.Name) > 18 {
 			if v.Name[0:19] == "xl/worksheets/sheet" {
-				var xlsx xlsxWorksheet
-				xml.Unmarshal([]byte(fileList[v.Name]), &xlsx)
-				xlsx = checkRow(xlsx)
-				output, _ := xml.Marshal(xlsx)
-				fileList[v.Name] = replaceWorkSheetsRelationshipsNameSpace(string(output))
 				worksheets++
 			}
 		}

+ 6 - 1
picture.go

@@ -33,13 +33,18 @@ import (
 //        xlsx := excelize.CreateFile()
 //        // Insert a picture.
 //        err := xlsx.AddPicture("Sheet1", "A2", "/tmp/image1.jpg", 0, 0, 1, 1)
+//        if err != nil {
+//            fmt.Println(err)
+//        }
 //        // Insert a picture to sheet with scaling.
 //        err = xlsx.AddPicture("Sheet1", "D2", "/tmp/image1.png", 0, 0, 0.5, 0.5)
+//        if err != nil {
+//            fmt.Println(err)
+//        }
 //        // Insert a picture offset in the cell.
 //        err = xlsx.AddPicture("Sheet1", "H2", "/tmp/image3.gif", 15, 10, 1, 1)
 //        if err != nil {
 //            fmt.Println(err)
-//            os.Exit(1)
 //        }
 //        err = xlsx.WriteTo("/tmp/Workbook.xlsx")
 //        if err != nil {

+ 29 - 19
rows.go

@@ -17,23 +17,37 @@ import (
 //    }
 //
 func (f *File) GetRows(sheet string) [][]string {
-	xlsx := xlsxWorksheet{}
-	r := [][]string{}
+	rows := [][]string{}
 	name := "xl/worksheets/" + strings.ToLower(sheet) + ".xml"
-	err := xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+	decoder := xml.NewDecoder(strings.NewReader(f.readXML(name)))
+	d, err := readXMLSST(f)
 	if err != nil {
-		return r
+		return rows
 	}
-	rows := xlsx.SheetData.Row
-	for _, row := range rows {
-		c := []string{}
-		for _, colCell := range row.C {
-			val, _ := colCell.getValueFrom(f)
-			c = append(c, val)
+	var inElement string
+	var row []string
+	for {
+		token, _ := decoder.Token()
+		if token == nil {
+			break
+		}
+		switch startElement := token.(type) {
+		case xml.StartElement:
+			inElement = startElement.Name.Local
+			if inElement == "row" {
+				var r xlsxRow
+				decoder.DecodeElement(&r, &startElement)
+				for _, colCell := range r.C {
+					val, _ := colCell.getValueFrom(f, d)
+					row = append(row, val)
+				}
+				rows = append(rows, row)
+				row = row[:0]
+			}
+		default:
 		}
-		r = append(r, c)
 	}
-	return r
+	return rows
 }
 
 // SetRowHeight provides a function to set the height of a single row.
@@ -65,23 +79,19 @@ func (f *File) SetRowHeight(sheet string, rowIndex int, height float64) {
 }
 
 // readXMLSST read xmlSST simple function.
-func readXMLSST(f *File) (xlsxSST, error) {
+func readXMLSST(f *File) (*xlsxSST, error) {
 	shardStrings := xlsxSST{}
 	err := xml.Unmarshal([]byte(f.readXML("xl/sharedStrings.xml")), &shardStrings)
-	return shardStrings, err
+	return &shardStrings, err
 }
 
 // getValueFrom return a value from a column/row cell, this function is inteded
 // to be used with for range on rows an argument with the xlsx opened file.
-func (xlsx *xlsxC) getValueFrom(f *File) (string, error) {
+func (xlsx *xlsxC) getValueFrom(f *File, d *xlsxSST) (string, error) {
 	switch xlsx.T {
 	case "s":
 		xlsxSI := 0
 		xlsxSI, _ = strconv.Atoi(xlsx.V)
-		d, err := readXMLSST(f)
-		if err != nil {
-			return "", err
-		}
 		if len(d.SI[xlsxSI].R) > 0 {
 			value := ""
 			for _, v := range d.SI[xlsxSI].R {