From b7fece51736977e7d84aca30ecce7f6b3a1251f2 Mon Sep 17 00:00:00 2001 From: xuri Date: Tue, 6 Jul 2021 00:31:04 +0800 Subject: [PATCH] Support concurrency add picture --- calcchain.go | 2 ++ cell_test.go | 5 +++++ comment.go | 2 ++ drawing.go | 9 +++++++-- excelize.go | 8 +++++++- picture.go | 16 ++++++++++++++++ rows.go | 2 ++ sheet.go | 8 ++++++++ xmlContentTypes.go | 6 +++++- xmlDrawing.go | 6 +++++- xmlWorkbook.go | 6 +++++- 11 files changed, 64 insertions(+), 6 deletions(-) diff --git a/calcchain.go b/calcchain.go index 1b99a04..671d144 100644 --- a/calcchain.go +++ b/calcchain.go @@ -56,6 +56,8 @@ func (f *File) deleteCalcChain(index int, axis string) { f.CalcChain = nil f.Pkg.Delete("xl/calcChain.xml") content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for k, v := range content.Overrides { if v.PartName == "/xl/calcChain.xml" { content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...) diff --git a/cell_test.go b/cell_test.go index e289983..91f4804 100644 --- a/cell_test.go +++ b/cell_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + _ "image/jpeg" + "github.com/stretchr/testify/assert" ) @@ -25,6 +27,9 @@ func TestConcurrency(t *testing.T) { 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 add picture + assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"), + `{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`)) // Concurrency get cell picture name, raw, err := f.GetPicture("Sheet1", "A1") assert.Equal(t, "", name) diff --git a/comment.go b/comment.go index 306826d..a89d2bb 100644 --- a/comment.go +++ b/comment.go @@ -76,6 +76,8 @@ func (f *File) GetComments() (comments map[string][]Comment) { func (f *File) getSheetComments(sheetFile string) string { var rels = "xl/worksheets/_rels/" + sheetFile + ".rels" if sheetRels := f.relsReader(rels); sheetRels != nil { + sheetRels.Lock() + defer sheetRels.Unlock() for _, v := range sheetRels.Relationships { if v.Type == SourceRelationshipComments { return v.Target diff --git a/drawing.go b/drawing.go index 181bb43..86d5ca6 100644 --- a/drawing.go +++ b/drawing.go @@ -1173,8 +1173,13 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) { } f.Drawings.Store(path, &content) } - wsDr, _ := f.Drawings.Load(path) - return wsDr.(*xlsxWsDr), len(wsDr.(*xlsxWsDr).OneCellAnchor) + len(wsDr.(*xlsxWsDr).TwoCellAnchor) + 2 + var wsDr *xlsxWsDr + if drawing, ok := f.Drawings.Load(path); ok && drawing != nil { + wsDr = drawing.(*xlsxWsDr) + } + wsDr.Lock() + defer wsDr.Unlock() + return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2 } // addDrawingChart provides a function to add chart graphic frame by given diff --git a/excelize.go b/excelize.go index bdb7120..0091c82 100644 --- a/excelize.go +++ b/excelize.go @@ -43,7 +43,7 @@ type File struct { Path string SharedStrings *xlsxSST sharedStringsMap map[string]int - Sheet sync.Map // map[string]*xlsxWorksheet + Sheet sync.Map SheetCount int Styles *xlsxStyleSheet Theme *xlsxTheme @@ -257,6 +257,8 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int { if rels == nil { rels = &xlsxRelationships{} } + rels.Lock() + defer rels.Unlock() var rID int for idx, rel := range rels.Relationships { ID, _ := strconv.Atoi(strings.TrimPrefix(rel.ID, "rId")) @@ -357,6 +359,8 @@ func (f *File) AddVBAProject(bin string) error { } f.setContentTypePartVBAProjectExtensions() wb := f.relsReader(f.getWorkbookRelsPath()) + wb.Lock() + defer wb.Unlock() var rID int var ok bool for _, rel := range wb.Relationships { @@ -387,6 +391,8 @@ func (f *File) AddVBAProject(bin string) error { func (f *File) setContentTypePartVBAProjectExtensions() { var ok bool content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for _, v := range content.Defaults { if v.Extension == "bin" { ok = true diff --git a/picture.go b/picture.go index 58fa909..e524224 100644 --- a/picture.go +++ b/picture.go @@ -184,6 +184,8 @@ func (f *File) deleteSheetRelationships(sheet, rID string) { if sheetRels == nil { sheetRels = &xlsxRelationships{} } + sheetRels.Lock() + defer sheetRels.Unlock() for k, v := range sheetRels.Relationships { if v.ID == rID { sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...) @@ -297,6 +299,8 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he FLocksWithSheet: formatSet.FLocksWithSheet, FPrintsWithSheet: formatSet.FPrintsWithSheet, } + content.Lock() + defer content.Unlock() content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) f.Drawings.Store(drawingXML, content) return err @@ -344,6 +348,8 @@ func (f *File) addMedia(file []byte, ext string) string { func (f *File) setContentTypePartImageExtensions() { var imageTypes = map[string]bool{"jpeg": false, "png": false, "gif": false, "tiff": false} content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for _, v := range content.Defaults { _, ok := imageTypes[v.Extension] if ok { @@ -365,6 +371,8 @@ func (f *File) setContentTypePartImageExtensions() { func (f *File) setContentTypePartVMLExtensions() { vml := false content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for _, v := range content.Defaults { if v.Extension == "vml" { vml = true @@ -410,6 +418,8 @@ func (f *File) addContentTypePart(index int, contentType string) { s() } content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for _, v := range content.Overrides { if v.PartName == partNames[contentType] { return @@ -434,6 +444,8 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string { if sheetRels == nil { sheetRels = &xlsxRelationships{} } + sheetRels.Lock() + defer sheetRels.Unlock() for _, v := range sheetRels.Relationships { if v.ID == rID { return v.Target @@ -560,6 +572,8 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD anchor *xdrCellAnchor drawRel *xlsxRelationship ) + wsDr.Lock() + defer wsDr.Unlock() for _, anchor = range wsDr.TwoCellAnchor { if anchor.From != nil && anchor.Pic != nil { if anchor.From.Col == col && anchor.From.Row == row { @@ -584,6 +598,8 @@ func (f *File) getPictureFromWsDr(row, col int, drawingRelationships string, wsD // relationship ID. func (f *File) getDrawingRelationships(rels, rID string) *xlsxRelationship { if drawingRels := f.relsReader(rels); drawingRels != nil { + drawingRels.Lock() + defer drawingRels.Unlock() for _, v := range drawingRels.Relationships { if v.ID == rID { return &v diff --git a/rows.go b/rows.go index 229b12d..a40f4a9 100644 --- a/rows.go +++ b/rows.go @@ -272,6 +272,8 @@ func (f *File) SetRowHeight(sheet string, row int, height float64) error { // name and row number. func (f *File) getRowHeight(sheet string, row int) int { ws, _ := f.workSheetReader(sheet) + ws.Lock() + defer ws.Unlock() for i := range ws.SheetData.Row { v := &ws.SheetData.Row[i] if v.R == row && v.Ht != 0 { diff --git a/sheet.go b/sheet.go index 2b91495..3a55540 100644 --- a/sheet.go +++ b/sheet.go @@ -93,6 +93,8 @@ func (f *File) contentTypesWriter() { // the spreadsheet. func (f *File) getWorkbookPath() (path string) { if rels := f.relsReader("_rels/.rels"); rels != nil { + rels.Lock() + defer rels.Unlock() for _, rel := range rels.Relationships { if rel.Type == SourceRelationshipOfficeDocument { path = strings.TrimPrefix(rel.Target, "/") @@ -198,6 +200,8 @@ func trimCell(column []xlsxC) []xlsxC { // type of the spreadsheet. func (f *File) setContentTypes(partName, contentType string) { content := f.contentTypesReader() + content.Lock() + defer content.Unlock() content.Overrides = append(content.Overrides, xlsxOverride{ PartName: partName, ContentType: contentType, @@ -540,6 +544,8 @@ func (f *File) DeleteSheet(name string) { // relationships by given relationships ID in the file workbook.xml.rels. func (f *File) deleteSheetFromWorkbookRels(rID string) string { content := f.relsReader(f.getWorkbookRelsPath()) + content.Lock() + defer content.Unlock() for k, v := range content.Relationships { if v.ID == rID { content.Relationships = append(content.Relationships[:k], content.Relationships[k+1:]...) @@ -556,6 +562,8 @@ func (f *File) deleteSheetFromContentTypes(target string) { target = "/xl/" + target } content := f.contentTypesReader() + content.Lock() + defer content.Unlock() for k, v := range content.Overrides { if v.PartName == target { content.Overrides = append(content.Overrides[:k], content.Overrides[k+1:]...) diff --git a/xmlContentTypes.go b/xmlContentTypes.go index f429ef6..6b6db63 100644 --- a/xmlContentTypes.go +++ b/xmlContentTypes.go @@ -11,12 +11,16 @@ package excelize -import "encoding/xml" +import ( + "encoding/xml" + "sync" +) // xlsxTypes directly maps the types element of content types for relationship // parts, it takes a Multipurpose Internet Mail Extension (MIME) media type as a // value. type xlsxTypes struct { + sync.Mutex XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/content-types Types"` Overrides []xlsxOverride `xml:"Override"` Defaults []xlsxDefault `xml:"Default"` diff --git a/xmlDrawing.go b/xmlDrawing.go index 4b51b63..4e35fcf 100644 --- a/xmlDrawing.go +++ b/xmlDrawing.go @@ -11,7 +11,10 @@ package excelize -import "encoding/xml" +import ( + "encoding/xml" + "sync" +) // Source relationship and namespace list, associated prefixes and schema in which it was // introduced. @@ -303,6 +306,7 @@ type xlsxPoint2D struct { // xlsxWsDr directly maps the root element for a part of this content type shall // wsDr. type xlsxWsDr struct { + sync.Mutex XMLName xml.Name `xml:"xdr:wsDr"` AbsoluteAnchor []*xdrCellAnchor `xml:"xdr:absoluteAnchor"` OneCellAnchor []*xdrCellAnchor `xml:"xdr:oneCellAnchor"` diff --git a/xmlWorkbook.go b/xmlWorkbook.go index 7151c6f..0e8839b 100644 --- a/xmlWorkbook.go +++ b/xmlWorkbook.go @@ -11,10 +11,14 @@ package excelize -import "encoding/xml" +import ( + "encoding/xml" + "sync" +) // xlsxRelationships describe references from parts to other internal resources in the package or to external resources. type xlsxRelationships struct { + sync.Mutex XMLName xml.Name `xml:"http://schemas.openxmlformats.org/package/2006/relationships Relationships"` Relationships []xlsxRelationship `xml:"Relationship"` }