Browse Source

Build workbook.xml.rels file, and use sheetnames instead of positional indexes
in File.Sheets

Geoffrey J. Teale 11 years ago
parent
commit
ec05f53ae7
9 changed files with 82 additions and 58 deletions
  1. 22 13
      file.go
  2. 12 8
      file_test.go
  3. 23 21
      lib.go
  4. 18 11
      lib_test.go
  5. 1 1
      macExcel_test.go
  6. 3 1
      macNumbers_test.go
  7. 0 2
      sheet.go
  8. 2 0
      workbook.go
  9. 1 1
      wpsBlankLine_test.go

+ 22 - 13
file.go

@@ -3,6 +3,7 @@ package xlsx
 import (
 	"archive/zip"
 	"encoding/xml"
+	"fmt"
 )
 
 // File is a high level structure providing a slice of Sheet structs
@@ -11,16 +12,14 @@ type File struct {
 	worksheets     map[string]*zip.File
 	referenceTable *RefTable
 	styles         *xlsxStyles
-	Sheets         []*Sheet          // sheet access by index
-	Sheet          map[string]*Sheet // sheet access by name
+	Sheets        map[string]*Sheet          // sheet access by index
 }
 
 
 // Create a new File
 func NewFile() (file *File) {
 	file = &File{};
-	file.Sheets = make([]*Sheet, 0, 100)
-	file.Sheet = make(map[string]*Sheet)
+	file.Sheets = make(map[string]*Sheet)
 	return
 }
 
@@ -38,17 +37,16 @@ func OpenFile(filename string) (*File, error) {
 // Add a new Sheet, with the provided name, to a File
 func (f *File) AddSheet(sheetName string) (sheet *Sheet) {
 	sheet = &Sheet{}
-	f.Sheets = append(f.Sheets, sheet)
-	f.Sheet[sheetName] = sheet
+	f.Sheets[sheetName] = sheet
 	return sheet
 }
 
 
-func (f *File) MarshallParts() ([]string, error) {
-	var parts []string
+func (f *File) MarshallParts() (map[string]string, error) {
+	var parts map[string]string
 	var refTable *RefTable = NewSharedStringRefTable()
+	var workbookRels WorkBookRels = make(WorkBookRels)
 	var err error
-	var sheetCount int = len(f.Sheets)
 
 	marshal := func(thing interface{}) (string, error) {
 		body, err := xml.MarshalIndent(thing, "  ", "  ")
@@ -58,16 +56,27 @@ func (f *File) MarshallParts() ([]string, error) {
 		return xml.Header + string(body), nil
 	}
 
-	parts = make([]string, sheetCount + 5)
-	for i, sheet := range f.Sheets {
+	parts = make(map[string]string)
+	sheetIndex := 1
+	// _ here is sheet name.
+	for _, sheet := range f.Sheets {
 		xSheet := sheet.makeXLSXSheet(refTable)
-		parts[i], err = marshal(xSheet)
+		sheetId := fmt.Sprintf("rId%d", sheetIndex)
+		sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
+		workbookRels[sheetId] = sheetPath
+		parts[sheetPath], err = marshal(xSheet)
 		if err != nil {
 			return parts, err
 		}
+sheetIndex++
 	}
 	xSST := refTable.makeXLSXSST()
-	parts[sheetCount], err = marshal(xSST)
+	parts["xl/sharedStrings.xml"], err = marshal(xSST)
+	if err != nil {
+		return parts, err
+	}
+	xWRel := workbookRels.MakeXLSXWorkbookRels()
+	parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
 	if err != nil {
 		return parts, err
 	}

+ 12 - 8
file_test.go

@@ -40,7 +40,7 @@ func (l *FileSuite) TestCreateSheet(c *C) {
 	c.Assert(xlsxFile, NotNil)
 	sheetLen := len(xlsxFile.Sheets)
 	c.Assert(sheetLen, Equals, 3)
-	sheet = xlsxFile.Sheets[0]
+	sheet = xlsxFile.Sheets["Tabelle1"]
 	rowLen := len(sheet.Rows)
 	c.Assert(rowLen, Equals, 2)
 	row = sheet.Rows[0]
@@ -57,9 +57,7 @@ func (l *FileSuite) TestAddSheet(c *C) {
 	sheet := f.AddSheet("MySheet")
 	c.Assert(sheet, NotNil)
 	c.Assert(len(f.Sheets), Equals, 1)
-	c.Assert(f.Sheets[0], Equals, sheet)
-	c.Assert(len(f.Sheet), Equals, 1)
-	c.Assert(f.Sheet["MySheet"], Equals, sheet)
+	c.Assert(f.Sheets["MySheet"], Equals, sheet)
 }
 
 // Test that we can marshall a File to a collection of xml files
@@ -76,7 +74,7 @@ func (l *FileSuite) TestMarshalFile(c *C) {
 	cell2.Value = "A cell!"
 	parts, err := f.MarshallParts()
 	c.Assert(err, IsNil)
-	c.Assert(len(parts), Equals, 7)
+	c.Assert(len(parts), Equals, 4)
 	expectedSheet := `<?xml version="1.0" encoding="UTF-8"?>
   <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
     <dimension ref="A1:A1"></dimension>
@@ -88,14 +86,20 @@ func (l *FileSuite) TestMarshalFile(c *C) {
       </row>
     </sheetData>
   </worksheet>`
-	c.Assert(parts[0], Equals, expectedSheet)
-	c.Assert(parts[1], Equals, expectedSheet)
+	c.Assert(parts["worksheets/sheet1.xml"], Equals, expectedSheet)
+	c.Assert(parts["worksheets/sheet2.xml"], Equals, expectedSheet)
 	expectedXLSXSST := `<?xml version="1.0" encoding="UTF-8"?>
   <sst count="1" uniqueCount="1">
     <si>
       <t>A cell!</t>
     </si>
   </sst>`
-	c.Assert(parts[2], Equals, expectedXLSXSST)
+	c.Assert(parts["xl/sharedStrings.xml"], Equals, expectedXLSXSST)
+	expectedXLSXWorkbookRels := `<?xml version="1.0" encoding="UTF-8"?>
+  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+    <Relationship Id="rId1" Target="worksheets/sheet1.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"></Relationship>
+    <Relationship Id="rId2" Target="worksheets/sheet2.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"></Relationship>
+  </Relationships>`
+	c.Assert(parts["xl/_rels/workbook.xml.rels"], Equals, expectedXLSXWorkbookRels)
 
 }

+ 23 - 21
lib.go

@@ -371,7 +371,7 @@ func readSheetFromFile(sc chan *indexedSheet, index int, rsheet xlsxSheet, fi *F
 // readSheetsFromZipFile is an internal helper function that loops
 // over the Worksheets defined in the XSLXWorkbook and loads them into
 // Sheet objects stored in the Sheets slice of a xlsx.File struct.
-func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]string) ([]*Sheet, []string, error) {
+func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]string) (map[string]*Sheet, error) {
 	var workbook *xlsxWorkbook
 	var error error
 	var rc io.ReadCloser
@@ -380,16 +380,15 @@ func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]strin
 	workbook = new(xlsxWorkbook)
 	rc, error = f.Open()
 	if error != nil {
-		return nil, nil, error
+		return nil, error
 	}
 	decoder = xml.NewDecoder(rc)
 	error = decoder.Decode(workbook)
 	if error != nil {
-		return nil, nil, error
+		return nil, error
 	}
 	sheetCount = len(workbook.Sheets.Sheet)
-	sheets := make([]*Sheet, sheetCount)
-	names := make([]string, sheetCount)
+	sheets := make(map[string]*Sheet, sheetCount)
 	sheetChan := make(chan *indexedSheet, sheetCount)
 	for i, rawsheet := range workbook.Sheets.Sheet {
 		go readSheetFromFile(sheetChan, i, rawsheet, file, sheetXMLMap)
@@ -397,12 +396,11 @@ func readSheetsFromZipFile(f *zip.File, file *File, sheetXMLMap map[string]strin
 	for j := 0; j < sheetCount; j++ {
 		sheet := <-sheetChan
 		if sheet.Error != nil {
-			return nil, nil, sheet.Error
+			return nil, sheet.Error
 		}
-		sheets[sheet.Index] = sheet.Sheet
-		names[sheet.Index] = workbook.Sheets.Sheet[sheet.Index].Name
+		sheets[workbook.Sheets.Sheet[sheet.Index].Name] = sheet.Sheet
 	}
-	return sheets, names, nil
+	return sheets, nil
 }
 
 // readSharedStringsFromZipFile() is an internal helper function to
@@ -452,10 +450,21 @@ func readStylesFromZipFile(f *zip.File) (*xlsxStyles, error) {
 
 type WorkBookRels map[string]string
 
-func (w *WorkBookRels) Marshal() string {
-	return ""
+func (w *WorkBookRels) MakeXLSXWorkbookRels() xlsxWorkbookRels {
+	xWorkbookRels := xlsxWorkbookRels{}
+	xWorkbookRels.Relationships = make([]xlsxWorkbookRelation, len(*w))
+	index := 0
+	for k, v := range(*w) {
+		xWorkbookRels.Relationships[index] = xlsxWorkbookRelation{
+			Id: k,
+			Target: v,
+			Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"}
+		index++
+	}
+	return xWorkbookRels
 }
 
+
 // readWorkbookRelationsFromZipFile is an internal helper function to
 // extract a map of relationship ID strings to the name of the
 // worksheet.xml file they refer to.  The resulting map can be used to
@@ -496,16 +505,14 @@ func ReadZip(f *zip.ReadCloser) (*File, error) {
 }
 
 // ReadZipReader() can be used to read an XLSX in memory without
-// touching thes filesystem.
+// touching the filesystem.
 func ReadZipReader(r *zip.Reader) (*File, error) {
 	var err error
 	var file *File
-	var names []string
 	var reftable *RefTable
 	var sharedStrings *zip.File
-	var sheetMap map[string]*Sheet
 	var sheetXMLMap map[string]string
-	var sheets []*Sheet
+	var sheets map[string]*Sheet
 	var style *xlsxStyles
 	var styles *zip.File
 	var v *zip.File
@@ -553,7 +560,7 @@ func ReadZipReader(r *zip.Reader) (*File, error) {
 		return nil, err
 	}
 	file.styles = style
-	sheets, names, err = readSheetsFromZipFile(workbook, file, sheetXMLMap)
+	sheets, err = readSheetsFromZipFile(workbook, file, sheetXMLMap)
 	if err != nil {
 		return nil, err
 	}
@@ -563,11 +570,6 @@ func ReadZipReader(r *zip.Reader) (*File, error) {
 		return nil, readerErr
 	}
 	file.Sheets = sheets
-	sheetMap = make(map[string]*Sheet, len(names))
-	for i := 0; i < len(names); i++ {
-		sheetMap[names[i]] = sheets[i]
-	}
-	file.Sheet = sheetMap
 	return file, nil
 }
 

+ 18 - 11
lib_test.go

@@ -117,8 +117,10 @@ func (l *LibSuite) TestReadWorkbookRelationsFromZipFile(c *C) {
 
 	xlsxFile, err = OpenFile("testfile.xlsx")
 	c.Assert(err, IsNil)
-	sheetCount := len(xlsxFile.Sheet)
-	c.Assert(sheetCount, Equals, 3)
+	c.Assert(len(xlsxFile.Sheets), Equals, 3)
+	sheet, ok := xlsxFile.Sheets["Tabelle1"]
+	c.Assert(ok, Equals, true)
+	c.Assert(sheet, NotNil)
 }
 
 // which they are contained from the XLSX file, even when the
@@ -129,9 +131,7 @@ func (l *LibSuite) TestReadWorkbookRelationsFromZipFileWithFunnyNames(c *C) {
 
 	xlsxFile, err = OpenFile("testrels.xlsx")
 	c.Assert(err, IsNil)
-	sheetCount := len(xlsxFile.Sheet)
-	c.Assert(sheetCount, Equals, 2)
-	bob := xlsxFile.Sheet["Bob"]
+	bob := xlsxFile.Sheets["Bob"]
 	row1 := bob.Rows[0]
 	cell1 := row1.Cells[0]
 	c.Assert(cell1.String(), Equals, "I am Bob")
@@ -142,12 +142,19 @@ func (l *LibSuite) TestReadWorkbookRelationsFromZipFileWithFunnyNames(c *C) {
 func (l *LibSuite) TestWorkBookRelsMarshal(c *C) {
 	var rels WorkBookRels = make(WorkBookRels)
 	rels["rId1"] = "worksheets/sheet.xml"
-	expectedXML := `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
-  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="worksheets/sheet.xml"/>
-</Relationships>
-`
-	c.Assert(rels.Marshal(), Equals, expectedXML)
+	expectedXML := `<?xml version="1.0" encoding="UTF-8"?>
+  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+    <Relationship Id="rId1" Target="worksheets/sheet.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"></Relationship>
+  </Relationships>`
+	xRels := rels.MakeXLSXWorkbookRels()
+
+	output := bytes.NewBufferString(xml.Header)
+	body, err := xml.MarshalIndent(xRels, "  ", "  ")
+	c.Assert(err, IsNil)
+	c.Assert(body, NotNil)
+	_, err = output.Write(body)
+	c.Assert(err, IsNil)
+	c.Assert(output.String(), Equals, expectedXML)
 }
 
 

+ 1 - 1
macExcel_test.go

@@ -17,6 +17,6 @@ func (m *MacExcelSuite) TestMacExcel(c *C) {
 	xlsxFile, err := OpenFile("macExcelTest.xlsx")
 	c.Assert(err, IsNil)
 	c.Assert(xlsxFile, NotNil)
-	s := xlsxFile.Sheets[0].Cell(0, 0).String()
+	s := xlsxFile.Sheets["普通技能"].Cell(0, 0).String()
 	c.Assert(s, Equals, "编号")
 }

+ 3 - 1
macNumbers_test.go

@@ -14,6 +14,8 @@ func (m *MacNumbersSuite) TestMacNumbers(c *C) {
 	xlsxFile, err := OpenFile("macNumbersTest.xlsx")
 	c.Assert(err, IsNil)
 	c.Assert(xlsxFile, NotNil)
-	s := xlsxFile.Sheets[0].Cell(0, 0).String()
+	sheet, ok := xlsxFile.Sheets["主动技能"]
+	c.Assert(ok, Equals, true)
+	s := sheet.Cell(0, 0).String()
 	c.Assert(s, Equals, "编号")
 }

+ 0 - 2
sheet.go

@@ -1,8 +1,6 @@
 package xlsx
 
 import (
-	// "bytes"
-	// "encoding/xml"
 	"fmt"
 	"strconv"
 )

+ 2 - 0
workbook.go

@@ -10,6 +10,7 @@ import (
 // xmlxWorkbookRels contains xmlxWorkbookRelations
 // which maps sheet id and sheet XML
 type xlsxWorkbookRels struct {
+	XMLName		xml.Name	`xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"`
 	Relationships []xlsxWorkbookRelation `xml:"Relationship"`
 }
 
@@ -17,6 +18,7 @@ type xlsxWorkbookRels struct {
 type xlsxWorkbookRelation struct {
 	Id     string `xml:",attr"`
 	Target string `xml:",attr"`
+	Type	string `xml:",attr"`
 }
 
 // xlsxWorkbook directly maps the workbook element from the namespace

+ 1 - 1
wpsBlankLine_test.go

@@ -13,7 +13,7 @@ func (w *WpsBlankLineSuite) TestWpsBlankLine(c *C) {
 	xlsxFile, err := OpenFile("wpsBlankLineTest.xlsx")
 	c.Assert(err, IsNil)
 	c.Assert(xlsxFile, NotNil)
-	sheet := xlsxFile.Sheets[0]
+	sheet := xlsxFile.Sheets["Sheet1"]
 	row := sheet.Rows[0]
 	cell := row.Cells[0]
 	s := cell.String()