diff --git a/cell_test.go b/cell_test.go index 51ee956..2282e55 100644 --- a/cell_test.go +++ b/cell_test.go @@ -14,15 +14,23 @@ import ( ) func TestConcurrency(t *testing.T) { - f := NewFile() + f, err := OpenFile(filepath.Join("test", "Book1.xlsx")) + assert.NoError(t, err) wg := new(sync.WaitGroup) for i := 1; i <= 5; i++ { wg.Add(1) go func(val int, t *testing.T) { + // Concurrency set cell value assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("A%d", val), val)) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val))) _, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val)) assert.NoError(t, err) + // Concurrency get cell picture + name, raw, err := f.GetPicture("Sheet1", "A1") + assert.Equal(t, "", name) + assert.Nil(t, raw) + assert.NoError(t, err) + wg.Done() }(i, t) } diff --git a/drawing.go b/drawing.go index 83ec85b..58e2669 100644 --- a/drawing.go +++ b/drawing.go @@ -1146,8 +1146,8 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) { err error ok bool ) - - if f.Drawings[path] == nil { + _, ok = f.Drawings.Load(path) + if !ok { content := xlsxWsDr{} content.A = NameSpaceDrawingML.Value content.Xdr = NameSpaceDrawingMLSpreadSheet.Value @@ -1171,10 +1171,10 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) { }) } } - f.Drawings[path] = &content + f.Drawings.Store(path, &content) } - wsDr := f.Drawings[path] - return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2 + wsDr, _ := f.Drawings.Load(path) + return wsDr.(*xlsxWsDr), len(wsDr.(*xlsxWsDr).OneCellAnchor) + len(wsDr.(*xlsxWsDr).TwoCellAnchor) + 2 } // addDrawingChart provides a function to add chart graphic frame by given @@ -1232,7 +1232,7 @@ func (f *File) addDrawingChart(sheet, drawingXML, cell string, width, height, rI FPrintsWithSheet: formatSet.FPrintsWithSheet, } content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) - f.Drawings[drawingXML] = content + f.Drawings.Store(drawingXML, content) return err } @@ -1272,7 +1272,7 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma FPrintsWithSheet: formatSet.FPrintsWithSheet, } content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor) - f.Drawings[drawingXML] = content + f.Drawings.Store(drawingXML, content) } // deleteDrawing provides a function to delete chart graphic frame by given by @@ -1313,6 +1313,6 @@ func (f *File) deleteDrawing(col, row int, drawingXML, drawingType string) (err } } } - f.Drawings[drawingXML] = wsDr + f.Drawings.Store(drawingXML, wsDr) return err } diff --git a/drawing_test.go b/drawing_test.go index 1ee8fae..3c0b619 100644 --- a/drawing_test.go +++ b/drawing_test.go @@ -12,12 +12,13 @@ package excelize import ( + "sync" "testing" ) func TestDrawingParser(t *testing.T) { f := File{ - Drawings: make(map[string]*xlsxWsDr), + Drawings: sync.Map{}, XLSX: map[string][]byte{ "charset": MacintoshCyrillicCharset, "wsDr": []byte(``)}, diff --git a/excelize.go b/excelize.go index 940acf1..66cfd00 100644 --- a/excelize.go +++ b/excelize.go @@ -39,7 +39,7 @@ type File struct { CalcChain *xlsxCalcChain Comments map[string]*xlsxComments ContentTypes *xlsxTypes - Drawings map[string]*xlsxWsDr + Drawings sync.Map Path string SharedStrings *xlsxSST sharedStringsMap map[string]int @@ -50,7 +50,7 @@ type File struct { DecodeVMLDrawing map[string]*decodeVmlDrawing VMLDrawing map[string]*vmlDrawing WorkBook *xlsxWorkbook - Relationships map[string]*xlsxRelationships + Relationships sync.Map XLSX map[string][]byte CharsetReader charsetTranscoderFn } @@ -93,12 +93,12 @@ func newFile() *File { checked: make(map[string]bool), sheetMap: make(map[string]string), Comments: make(map[string]*xlsxComments), - Drawings: make(map[string]*xlsxWsDr), + Drawings: sync.Map{}, sharedStringsMap: make(map[string]int), Sheet: make(map[string]*xlsxWorksheet), DecodeVMLDrawing: make(map[string]*decodeVmlDrawing), VMLDrawing: make(map[string]*vmlDrawing), - Relationships: make(map[string]*xlsxRelationships), + Relationships: sync.Map{}, CharsetReader: charset.NewReaderLabel, } } @@ -277,7 +277,7 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int { Target: target, TargetMode: targetMode, }) - f.Relationships[relPath] = rels + f.Relationships.Store(relPath, rels) return rID } diff --git a/excelize_test.go b/excelize_test.go index ba7b528..e3cfa53 100644 --- a/excelize_test.go +++ b/excelize_test.go @@ -975,7 +975,7 @@ func TestGetActiveSheetIndex(t *testing.T) { func TestRelsWriter(t *testing.T) { f := NewFile() - f.Relationships["xl/worksheets/sheet/rels/sheet1.xml.rel"] = &xlsxRelationships{} + f.Relationships.Store("xl/worksheets/sheet/rels/sheet1.xml.rel", &xlsxRelationships{}) f.relsWriter() } @@ -1231,7 +1231,7 @@ func TestRelsReader(t *testing.T) { // Test unsupported charset. f := NewFile() rels := "xl/_rels/workbook.xml.rels" - f.Relationships[rels] = nil + f.Relationships.Store(rels, nil) f.XLSX[rels] = MacintoshCyrillicCharset f.relsReader(rels) } @@ -1239,7 +1239,7 @@ func TestRelsReader(t *testing.T) { func TestDeleteSheetFromWorkbookRels(t *testing.T) { f := NewFile() rels := "xl/_rels/workbook.xml.rels" - f.Relationships[rels] = nil + f.Relationships.Store(rels, nil) assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "") } diff --git a/file.go b/file.go index 495718a..fa73ec8 100644 --- a/file.go +++ b/file.go @@ -17,6 +17,7 @@ import ( "fmt" "io" "os" + "sync" ) // NewFile provides a function to create new file by default template. For @@ -40,13 +41,13 @@ func NewFile() *File { f.CalcChain = f.calcChainReader() f.Comments = make(map[string]*xlsxComments) f.ContentTypes = f.contentTypesReader() - f.Drawings = make(map[string]*xlsxWsDr) + f.Drawings = sync.Map{} f.Styles = f.stylesReader() f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing) f.VMLDrawing = make(map[string]*vmlDrawing) f.WorkBook = f.workbookReader() - f.Relationships = make(map[string]*xlsxRelationships) - f.Relationships["xl/_rels/workbook.xml.rels"] = f.relsReader("xl/_rels/workbook.xml.rels") + f.Relationships = sync.Map{} + f.Relationships.Store("xl/_rels/workbook.xml.rels", f.relsReader("xl/_rels/workbook.xml.rels")) f.Sheet["xl/worksheets/sheet1.xml"], _ = f.workSheetReader("Sheet1") f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml" f.Theme = f.themeReader() diff --git a/picture.go b/picture.go index 052ec83..09283c8 100644 --- a/picture.go +++ b/picture.go @@ -189,7 +189,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) { sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...) } } - f.Relationships[rels] = sheetRels + f.Relationships.Store(rels, sheetRels) } // addSheetLegacyDrawing provides a function to add legacy drawing element to @@ -228,11 +228,12 @@ func (f *File) countDrawings() int { c1++ } } - for rel := range f.Drawings { - if strings.Contains(rel, "xl/drawings/drawing") { + f.Drawings.Range(func(rel, value interface{}) bool { + if strings.Contains(rel.(string), "xl/drawings/drawing") { c2++ } - } + return true + }) if c1 < c2 { return c2 } @@ -296,7 +297,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he FPrintsWithSheet: formatSet.FPrintsWithSheet, } content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) - f.Drawings[drawingXML] = content + f.Drawings.Store(drawingXML, content) return err } @@ -582,12 +583,13 @@ func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship { // drawingsWriter provides a function to save xl/drawings/drawing%d.xml after // serialize structure. func (f *File) drawingsWriter() { - for path, d := range f.Drawings { + f.Drawings.Range(func(path, d interface{}) bool { if d != nil { - v, _ := xml.Marshal(d) - f.saveFileList(path, v) + v, _ := xml.Marshal(d.(*xlsxWsDr)) + f.saveFileList(path.(string), v) } - } + return true + }) } // drawingResize calculate the height and width after resizing. diff --git a/shape.go b/shape.go index f7e2ef3..e58d5cf 100644 --- a/shape.go +++ b/shape.go @@ -436,7 +436,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format FPrintsWithSheet: formatSet.Format.FPrintsWithSheet, } content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) - f.Drawings[drawingXML] = content + f.Drawings.Store(drawingXML, content) return err } diff --git a/sheet.go b/sheet.go index 555f8e3..420235c 100644 --- a/sheet.go +++ b/sheet.go @@ -231,15 +231,16 @@ func (f *File) setWorkbook(name string, sheetID, rid int) { // relsWriter provides a function to save relationships after // serialize structure. func (f *File) relsWriter() { - for path, rel := range f.Relationships { + f.Relationships.Range(func(path, rel interface{}) bool { if rel != nil { - output, _ := xml.Marshal(rel) - if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") { - output = f.replaceNameSpaceBytes(path, output) + output, _ := xml.Marshal(rel.(*xlsxRelationships)) + if strings.HasPrefix(path.(string), "xl/worksheets/sheet/rels/sheet") { + output = f.replaceNameSpaceBytes(path.(string), output) } - f.saveFileList(path, replaceRelationshipsBytes(output)) + f.saveFileList(path.(string), replaceRelationshipsBytes(output)) } - } + return true + }) } // setAppXML update docProps/app.xml file of XML. @@ -359,22 +360,6 @@ func (f *File) SetSheetName(oldName, newName string) { } } -// getSheetNameByID provides a function to get worksheet name of the -// spreadsheet by given worksheet ID. If given sheet ID is invalid, will -// return an empty string. -func (f *File) getSheetNameByID(ID int) string { - wb := f.workbookReader() - if wb == nil || ID < 1 { - return "" - } - for _, sheet := range wb.Sheets.Sheet { - if ID == sheet.SheetID { - return sheet.Name - } - } - return "" -} - // GetSheetName provides a function to get the sheet name of the workbook by // the given sheet index. If the given sheet index is invalid, it will return // an empty string. @@ -541,7 +526,7 @@ func (f *File) DeleteSheet(name string) { delete(f.sheetMap, sheetName) delete(f.XLSX, sheetXML) delete(f.XLSX, rels) - delete(f.Relationships, rels) + f.Relationships.Delete(rels) delete(f.Sheet, sheetXML) delete(f.xmlAttr, sheetXML) f.SheetCount-- @@ -1727,8 +1712,8 @@ func (f *File) RemovePageBreak(sheet, cell string) (err error) { // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels. func (f *File) relsReader(path string) *xlsxRelationships { var err error - - if f.Relationships[path] == nil { + rels, _ := f.Relationships.Load(path) + if rels == nil { _, ok := f.XLSX[path] if ok { c := xlsxRelationships{} @@ -1736,11 +1721,13 @@ func (f *File) relsReader(path string) *xlsxRelationships { Decode(&c); err != nil && err != io.EOF { log.Printf("xml decode error: %s", err) } - f.Relationships[path] = &c + f.Relationships.Store(path, &c) } } - - return f.Relationships[path] + if rels, _ = f.Relationships.Load(path); rels != nil { + return rels.(*xlsxRelationships) + } + return nil } // fillSheetData ensures there are enough rows, and columns in the chosen