Преглед изворни кода

Open file error return added and UpdateLinkedValue function added to fix linked values within a spreadsheet are not updating.

Ri Xu пре 9 година
родитељ
комит
df8f85d6ab
5 измењених фајлова са 101 додато и 27 уклоњено
  1. 14 5
      README.md
  2. 57 8
      excelize.go
  3. 21 11
      excelize_test.go
  4. 9 3
      lib.go
  5. BIN
      test/Workbook1.xlsx

+ 14 - 5
README.md

@@ -34,12 +34,15 @@ import (
 )
 
 func main() {
-    xlsx := excelize.CreateFile()
+    xlsx, err := excelize.CreateFile()
+    if err != nil {
+        fmt.Println(err)
+    }
     xlsx.NewSheet(2, "Sheet2")
     xlsx.NewSheet(3, "Sheet3")
     xlsx.SetCellInt("Sheet2", "A23", 10)
     xlsx.SetCellStr("Sheet3", "B20", "Hello")
-    err := xlsx.WriteTo("/home/Workbook.xlsx")
+    err = xlsx.WriteTo("/home/Workbook.xlsx")
     if err != nil {
         fmt.Println(err)
     }
@@ -59,14 +62,17 @@ import (
 )
 
 func main() {
-    xlsx := excelize.OpenFile("/home/Workbook.xlsx")
+    xlsx, err := excelize.OpenFile("/home/Workbook.xlsx")
+    if err != nil {
+        fmt.Println(err)
+    }
     xlsx.SetCellInt("Sheet2", "B2", 100)
     xlsx.SetCellStr("Sheet2", "C11", "Hello")
     xlsx.NewSheet(3, "TestSheet")
     xlsx.SetCellInt("Sheet3", "A23", 10)
     xlsx.SetCellStr("Sheet3", "b230", "World")
     xlsx.SetActiveSheet(2)
-    err := xlsx.Save()
+    err = xlsx.Save()
     if err != nil {
         fmt.Println(err)
     }
@@ -84,7 +90,10 @@ import (
 )
 
 func main() {
-    xlsx := excelize.OpenFile("/home/Workbook.xlsx")
+    xlsx, err := excelize.OpenFile("/home/Workbook.xlsx")
+    if err != nil {
+        fmt.Println(err)
+    }
     cell := xlsx.GetCellValue("Sheet2", "D11")
     fmt.Println(cell)
 }

+ 57 - 8
excelize.go

@@ -11,21 +11,28 @@ import (
 
 // File define a populated xlsx.File struct.
 type File struct {
-	XLSX map[string]string
-	Path string
+	XLSX       map[string]string
+	Path       string
+	SheetCount int
 }
 
 // OpenFile take the name of an XLSX file and returns a populated
 // xlsx.File struct for it.
-func OpenFile(filename string) *File {
+func OpenFile(filename string) (*File, error) {
 	var f *zip.ReadCloser
+	var err error
 	file := make(map[string]string)
-	f, _ = zip.OpenReader(filename)
-	file, _ = ReadZip(f)
-	return &File{
-		XLSX: file,
-		Path: filename,
+	sheetCount := 0
+	f, err = zip.OpenReader(filename)
+	if err != nil {
+		return &File{}, err
 	}
+	file, sheetCount, err = ReadZip(f)
+	return &File{
+		XLSX:       file,
+		Path:       filename,
+		SheetCount: sheetCount,
+	}, nil
 }
 
 // SetCellInt provide function to set int type value of a cell
@@ -176,6 +183,7 @@ func replaceWorkSheetsRelationshipsNameSpace(workbookMarshal string) string {
 //      <c r="G15" s="1" />
 //  </row>
 //
+// Noteice: this method could be very slow for large spreadsheets (more than 3000 rows one sheet).
 func checkRow(xlsx xlsxWorksheet) xlsxWorksheet {
 	buffer := bytes.Buffer{}
 	for k, v := range xlsx.SheetData.Row {
@@ -207,3 +215,44 @@ func checkRow(xlsx xlsxWorksheet) xlsxWorksheet {
 	}
 	return xlsx
 }
+
+// UpdateLinkedValue fix linked values within a spreadsheet are not updating.
+// This function will be remove value tag when met a cell have a linked value.
+// Reference https://social.technet.microsoft.com/Forums/office/en-US/e16bae1f-6a2c-4325-8013-e989a3479066/excel-2010-linked-cells-not-updating?forum=excel
+//
+// Notice: after open XLSX file Excel will be update linked value and generate
+// new value and will prompt save file or not.
+//
+// For example:
+//
+// <row r="19" spans="2:2">
+//     <c r="B19">
+//         <f>SUM(Sheet2!D2,Sheet2!D11)</f>
+//         <v>100</v>
+//     </c>
+// </row>
+//
+// to
+//
+// <row r="19" spans="2:2">
+//     <c r="B19">
+//         <f>SUM(Sheet2!D2,Sheet2!D11)</f>
+//     </c>
+// </row>
+func (f *File) UpdateLinkedValue() {
+	for i := 1; i <= f.SheetCount; i++ {
+		var xlsx xlsxWorksheet
+		name := `xl/worksheets/sheet` + strconv.Itoa(i) + `.xml`
+		xml.Unmarshal([]byte(f.readXML(name)), &xlsx)
+		for indexR, row := range xlsx.SheetData.Row {
+			for indexC, col := range row.C {
+				if col.F != nil && col.V != `` {
+					xlsx.SheetData.Row[indexR].C[indexC].V = ``
+					xlsx.SheetData.Row[indexR].C[indexC].T = ``
+				}
+			}
+		}
+		output, _ := xml.Marshal(xlsx)
+		f.saveFileList(name, replaceRelationshipsID(replaceWorkSheetsRelationshipsNameSpace(string(output))))
+	}
+}

+ 21 - 11
excelize_test.go

@@ -7,7 +7,11 @@ import (
 
 func TestExcelize(t *testing.T) {
 	// Test update a XLSX file
-	file := OpenFile("./test/Workbook1.xlsx")
+	file, err := OpenFile("./test/Workbook1.xlsx")
+	if err != nil {
+		t.Log(err)
+	}
+	file.UpdateLinkedValue()
 	file.SetCellInt("SHEET2", "B2", 100)
 	file.SetCellStr("SHEET2", "C11", "Knowns")
 	file.NewSheet(3, "TestSheet")
@@ -15,10 +19,20 @@ func TestExcelize(t *testing.T) {
 	file.SetCellStr("SHEET3", "b230", "10")
 	file.SetCellStr("SHEET10", "b230", "10")
 	file.SetActiveSheet(2)
+	// Test read cell value with given illegal rows number
+	file.GetCellValue("Sheet2", "a-1")
+	// Test read cell value with given lowercase column number
+	file.GetCellValue("Sheet2", "a5")
+	file.GetCellValue("Sheet2", "C11")
+	file.GetCellValue("Sheet2", "D11")
+	file.GetCellValue("Sheet2", "D12")
+	// Test read cell value with given axis large than exists row
+	file.GetCellValue("Sheet2", "E13")
+
 	for i := 1; i <= 300; i++ {
 		file.SetCellStr("SHEET3", "c"+strconv.Itoa(i), strconv.Itoa(i))
 	}
-	err := file.Save()
+	err = file.Save()
 	if err != nil {
 		t.Log(err)
 	}
@@ -45,13 +59,9 @@ func TestExcelize(t *testing.T) {
 		t.Log(err)
 	}
 
-	// Test read cell value with given illegal rows number
-	file.GetCellValue("Sheet2", "a-1")
-	// Test read cell value with given lowercase column number
-	file.GetCellValue("Sheet2", "a5")
-	file.GetCellValue("Sheet2", "C11")
-	file.GetCellValue("Sheet2", "D11")
-	file.GetCellValue("Sheet2", "D12")
-	// Test read cell value with given axis large than exists row
-	file.GetCellValue("Sheet2", "E13")
+	// Test open a XLSX file with given illegal path
+	_, err = OpenFile("./test/Workbook.xlsx")
+	if err != nil {
+		t.Log(err)
+	}
 }

+ 9 - 3
lib.go

@@ -14,19 +14,25 @@ import (
 // ReadZip takes a pointer to a zip.ReadCloser and returns a
 // xlsx.File struct populated with its contents. In most cases
 // ReadZip is not used directly, but is called internally by OpenFile.
-func ReadZip(f *zip.ReadCloser) (map[string]string, error) {
+func ReadZip(f *zip.ReadCloser) (map[string]string, int, error) {
 	defer f.Close()
 	return ReadZipReader(&f.Reader)
 }
 
 // ReadZipReader can be used to read an XLSX in memory without
 // touching the filesystem.
-func ReadZipReader(r *zip.Reader) (map[string]string, error) {
+func ReadZipReader(r *zip.Reader) (map[string]string, int, error) {
 	fileList := make(map[string]string)
+	worksheets := 0
 	for _, v := range r.File {
 		fileList[v.Name] = readFile(v)
+		if len(v.Name) > 18 {
+			if v.Name[0:19] == "xl/worksheets/sheet" {
+				worksheets++
+			}
+		}
 	}
-	return fileList, nil
+	return fileList, worksheets, nil
 }
 
 // Read XML content as string and replace drawing property in XML namespace of sheet

BIN
test/Workbook1.xlsx