Browse Source

Resolve #235, performance optimization for add comments (#347)

BluesJhao 6 years ago
parent
commit
1427027e38
3 changed files with 120 additions and 60 deletions
  1. 88 42
      comment.go
  2. 24 17
      excelize.go
  3. 8 1
      file.go

+ 88 - 42
comment.go

@@ -122,31 +122,34 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
 	row, _ := strconv.Atoi(strings.Map(intOnlyMapF, cell))
 	xAxis := row - 1
 	yAxis := TitleToNumber(col)
-	vml := vmlDrawing{
-		XMLNSv:  "urn:schemas-microsoft-com:vml",
-		XMLNSo:  "urn:schemas-microsoft-com:office:office",
-		XMLNSx:  "urn:schemas-microsoft-com:office:excel",
-		XMLNSmv: "http://macVmlSchemaUri",
-		Shapelayout: &xlsxShapelayout{
-			Ext: "edit",
-			IDmap: &xlsxIDmap{
-				Ext:  "edit",
-				Data: commentID,
-			},
-		},
-		Shapetype: &xlsxShapetype{
-			ID:        "_x0000_t202",
-			Coordsize: "21600,21600",
-			Spt:       202,
-			Path:      "m0,0l0,21600,21600,21600,21600,0xe",
-			Stroke: &xlsxStroke{
-				Joinstyle: "miter",
+	vml := f.VMLDrawing[drawingVML]
+	if vml == nil {
+		vml = &vmlDrawing{
+			XMLNSv:  "urn:schemas-microsoft-com:vml",
+			XMLNSo:  "urn:schemas-microsoft-com:office:office",
+			XMLNSx:  "urn:schemas-microsoft-com:office:excel",
+			XMLNSmv: "http://macVmlSchemaUri",
+			Shapelayout: &xlsxShapelayout{
+				Ext: "edit",
+				IDmap: &xlsxIDmap{
+					Ext:  "edit",
+					Data: commentID,
+				},
 			},
-			VPath: &vPath{
-				Gradientshapeok: "t",
-				Connecttype:     "miter",
+			Shapetype: &xlsxShapetype{
+				ID:        "_x0000_t202",
+				Coordsize: "21600,21600",
+				Spt:       202,
+				Path:      "m0,0l0,21600,21600,21600,21600,0xe",
+				Stroke: &xlsxStroke{
+					Joinstyle: "miter",
+				},
+				VPath: &vPath{
+					Gradientshapeok: "t",
+					Connecttype:     "miter",
+				},
 			},
-		},
+		}
 	}
 	sp := encodeShape{
 		Fill: &vFill{
@@ -191,10 +194,8 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
 		Strokecolor: "#edeaa1",
 		Val:         string(s[13 : len(s)-14]),
 	}
-	c, ok := f.XLSX[drawingVML]
-	if ok {
-		d := decodeVmlDrawing{}
-		_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
+	d := f.decodeVMLDrawingReader(drawingVML)
+	if d != nil {
 		for _, v := range d.Shape {
 			s := xlsxShape{
 				ID:          "_x0000_s1025",
@@ -208,8 +209,7 @@ func (f *File) addDrawingVML(commentID int, drawingVML, cell string, lineCount,
 		}
 	}
 	vml.Shape = append(vml.Shape, shape)
-	v, _ := xml.Marshal(vml)
-	f.XLSX[drawingVML] = v
+	f.VMLDrawing[drawingVML] = vml
 }
 
 // addComment provides a function to create chart as xl/comments%d.xml by
@@ -223,12 +223,15 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
 	if len(t) > 32512 {
 		t = t[0:32512]
 	}
-	comments := xlsxComments{
-		Authors: []xlsxAuthor{
-			{
-				Author: formatSet.Author,
+	comments := f.commentsReader(commentsXML)
+	if comments == nil {
+		comments = &xlsxComments{
+			Authors: []xlsxAuthor{
+				{
+					Author: formatSet.Author,
+				},
 			},
-		},
+		}
 	}
 	cmt := xlsxComment{
 		Ref:      cell,
@@ -261,15 +264,8 @@ func (f *File) addComment(commentsXML, cell string, formatSet *formatComment) {
 			},
 		},
 	}
-	c, ok := f.XLSX[commentsXML]
-	if ok {
-		d := xlsxComments{}
-		_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
-		comments.CommentList.Comment = append(comments.CommentList.Comment, d.CommentList.Comment...)
-	}
 	comments.CommentList.Comment = append(comments.CommentList.Comment, cmt)
-	v, _ := xml.Marshal(comments)
-	f.saveFileList(commentsXML, v)
+	f.Comments[commentsXML] = comments
 }
 
 // countComments provides a function to get comments files count storage in
@@ -283,3 +279,53 @@ func (f *File) countComments() int {
 	}
 	return count
 }
+
+// decodeVMLDrawingReader provides a function to get the pointer to the
+// structure after deserialization of xl/drawings/vmlDrawing%d.xml.
+func (f *File) decodeVMLDrawingReader(path string) *decodeVmlDrawing {
+	if f.DecodeVMLDrawing[path] == nil {
+		c, ok := f.XLSX[path]
+		if ok {
+			d := decodeVmlDrawing{}
+			_ = xml.Unmarshal(namespaceStrictToTransitional(c), &d)
+			f.DecodeVMLDrawing[path] = &d
+		}
+	}
+	return f.DecodeVMLDrawing[path]
+}
+
+// vmlDrawingWriter provides a function to save xl/drawings/vmlDrawing%d.xml.
+// after serialize structure.
+func (f *File) vmlDrawingWriter() {
+	for path, vml := range f.VMLDrawing {
+		if vml != nil {
+			v, _ := xml.Marshal(vml)
+			f.XLSX[path] = v
+		}
+	}
+}
+
+// commentsReader provides a function to get the pointer to the structure
+// after deserialization of xl/comments%d.xml.
+func (f *File) commentsReader(path string) *xlsxComments {
+	if f.Comments[path] == nil {
+		content, ok := f.XLSX[path]
+		if ok {
+			c := xlsxComments{}
+			_ = xml.Unmarshal(namespaceStrictToTransitional(content), &c)
+			f.Comments[path] = &c
+		}
+	}
+	return f.Comments[path]
+}
+
+// commentsWriter provides a function to save xl/comments%d.xml after
+// serialize structure.
+func (f *File) commentsWriter() {
+	for path, c := range f.Comments {
+		if c != nil {
+			v, _ := xml.Marshal(c)
+			f.saveFileList(path, v)
+		}
+	}
+}

+ 24 - 17
excelize.go

@@ -23,19 +23,22 @@ import (
 
 // File define a populated XLSX file struct.
 type File struct {
-	checked       map[string]bool
-	sheetMap      map[string]string
-	CalcChain     *xlsxCalcChain
-	ContentTypes  *xlsxTypes
-	Path          string
-	SharedStrings *xlsxSST
-	Sheet         map[string]*xlsxWorksheet
-	SheetCount    int
-	Styles        *xlsxStyleSheet
-	Theme         *xlsxTheme
-	WorkBook      *xlsxWorkbook
-	WorkBookRels  *xlsxWorkbookRels
-	XLSX          map[string][]byte
+	checked          map[string]bool
+	sheetMap         map[string]string
+	CalcChain        *xlsxCalcChain
+	Comments         map[string]*xlsxComments
+	ContentTypes     *xlsxTypes
+	Path             string
+	SharedStrings    *xlsxSST
+	Sheet            map[string]*xlsxWorksheet
+	SheetCount       int
+	Styles           *xlsxStyleSheet
+	Theme            *xlsxTheme
+	DecodeVMLDrawing map[string]*decodeVmlDrawing
+	VMLDrawing       map[string]*vmlDrawing
+	WorkBook         *xlsxWorkbook
+	WorkBookRels     *xlsxWorkbookRels
+	XLSX             map[string][]byte
 }
 
 // OpenFile take the name of an XLSX file and returns a populated XLSX file
@@ -71,11 +74,15 @@ func OpenReader(r io.Reader) (*File, error) {
 		return nil, err
 	}
 	f := &File{
-		checked:    make(map[string]bool),
-		Sheet:      make(map[string]*xlsxWorksheet),
-		SheetCount: sheetCount,
-		XLSX:       file,
+		checked:          make(map[string]bool),
+		Comments:         make(map[string]*xlsxComments),
+		Sheet:            make(map[string]*xlsxWorksheet),
+		SheetCount:       sheetCount,
+		DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
+		VMLDrawing:       make(map[string]*vmlDrawing),
+		XLSX:             file,
 	}
+	f.CalcChain = f.calcChainReader()
 	f.sheetMap = f.getSheetMap()
 	f.Styles = f.stylesReader()
 	f.Theme = f.themeReader()

+ 8 - 1
file.go

@@ -39,8 +39,12 @@ func NewFile() *File {
 		SheetCount: 1,
 		XLSX:       file,
 	}
+	f.CalcChain = f.calcChainReader()
+	f.Comments = make(map[string]*xlsxComments)
 	f.ContentTypes = f.contentTypesReader()
 	f.Styles = f.stylesReader()
+	f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
+	f.VMLDrawing = make(map[string]*vmlDrawing)
 	f.WorkBook = f.workbookReader()
 	f.WorkBookRels = f.workbookRelsReader()
 	f.Sheet["xl/worksheets/sheet1.xml"] = f.workSheetReader("Sheet1")
@@ -87,12 +91,15 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
 func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
 	buf := new(bytes.Buffer)
 	zw := zip.NewWriter(buf)
+	f.calcChainWriter()
+	f.commentsWriter()
 	f.contentTypesWriter()
+	f.vmlDrawingWriter()
 	f.workbookWriter()
 	f.workbookRelsWriter()
 	f.worksheetWriter()
 	f.styleSheetWriter()
-	f.calcChainWriter()
+
 	for path, content := range f.XLSX {
 		fi, err := zw.Create(path)
 		if err != nil {