This closes #861, support concurrency get cell picture and remove unused internal function `getSheetNameByID`

This commit is contained in:
xuri 2021-07-04 12:13:06 +08:00
parent 5ec61310dc
commit 0e02329bed
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
9 changed files with 58 additions and 59 deletions

View File

@ -14,15 +14,23 @@ import (
) )
func TestConcurrency(t *testing.T) { func TestConcurrency(t *testing.T) {
f := NewFile() f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
assert.NoError(t, err)
wg := new(sync.WaitGroup) wg := new(sync.WaitGroup)
for i := 1; i <= 5; i++ { for i := 1; i <= 5; i++ {
wg.Add(1) wg.Add(1)
go func(val int, t *testing.T) { 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("A%d", val), val))
assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val))) assert.NoError(t, f.SetCellValue("Sheet1", fmt.Sprintf("B%d", val), strconv.Itoa(val)))
_, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val)) _, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val))
assert.NoError(t, err) 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() wg.Done()
}(i, t) }(i, t)
} }

View File

@ -1146,8 +1146,8 @@ func (f *File) drawingParser(path string) (*xlsxWsDr, int) {
err error err error
ok bool ok bool
) )
_, ok = f.Drawings.Load(path)
if f.Drawings[path] == nil { if !ok {
content := xlsxWsDr{} content := xlsxWsDr{}
content.A = NameSpaceDrawingML.Value content.A = NameSpaceDrawingML.Value
content.Xdr = NameSpaceDrawingMLSpreadSheet.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] wsDr, _ := f.Drawings.Load(path)
return wsDr, len(wsDr.OneCellAnchor) + len(wsDr.TwoCellAnchor) + 2 return wsDr.(*xlsxWsDr), len(wsDr.(*xlsxWsDr).OneCellAnchor) + len(wsDr.(*xlsxWsDr).TwoCellAnchor) + 2
} }
// addDrawingChart provides a function to add chart graphic frame by given // 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, FPrintsWithSheet: formatSet.FPrintsWithSheet,
} }
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content f.Drawings.Store(drawingXML, content)
return err return err
} }
@ -1272,7 +1272,7 @@ func (f *File) addSheetDrawingChart(drawingXML string, rID int, formatSet *forma
FPrintsWithSheet: formatSet.FPrintsWithSheet, FPrintsWithSheet: formatSet.FPrintsWithSheet,
} }
content.AbsoluteAnchor = append(content.AbsoluteAnchor, &absoluteAnchor) 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 // 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 return err
} }

View File

@ -12,12 +12,13 @@
package excelize package excelize
import ( import (
"sync"
"testing" "testing"
) )
func TestDrawingParser(t *testing.T) { func TestDrawingParser(t *testing.T) {
f := File{ f := File{
Drawings: make(map[string]*xlsxWsDr), Drawings: sync.Map{},
XLSX: map[string][]byte{ XLSX: map[string][]byte{
"charset": MacintoshCyrillicCharset, "charset": MacintoshCyrillicCharset,
"wsDr": []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`)}, "wsDr": []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"><xdr:oneCellAnchor><xdr:graphicFrame/></xdr:oneCellAnchor></xdr:wsDr>`)},

View File

@ -39,7 +39,7 @@ type File struct {
CalcChain *xlsxCalcChain CalcChain *xlsxCalcChain
Comments map[string]*xlsxComments Comments map[string]*xlsxComments
ContentTypes *xlsxTypes ContentTypes *xlsxTypes
Drawings map[string]*xlsxWsDr Drawings sync.Map
Path string Path string
SharedStrings *xlsxSST SharedStrings *xlsxSST
sharedStringsMap map[string]int sharedStringsMap map[string]int
@ -50,7 +50,7 @@ type File struct {
DecodeVMLDrawing map[string]*decodeVmlDrawing DecodeVMLDrawing map[string]*decodeVmlDrawing
VMLDrawing map[string]*vmlDrawing VMLDrawing map[string]*vmlDrawing
WorkBook *xlsxWorkbook WorkBook *xlsxWorkbook
Relationships map[string]*xlsxRelationships Relationships sync.Map
XLSX map[string][]byte XLSX map[string][]byte
CharsetReader charsetTranscoderFn CharsetReader charsetTranscoderFn
} }
@ -93,12 +93,12 @@ func newFile() *File {
checked: make(map[string]bool), checked: make(map[string]bool),
sheetMap: make(map[string]string), sheetMap: make(map[string]string),
Comments: make(map[string]*xlsxComments), Comments: make(map[string]*xlsxComments),
Drawings: make(map[string]*xlsxWsDr), Drawings: sync.Map{},
sharedStringsMap: make(map[string]int), sharedStringsMap: make(map[string]int),
Sheet: make(map[string]*xlsxWorksheet), Sheet: make(map[string]*xlsxWorksheet),
DecodeVMLDrawing: make(map[string]*decodeVmlDrawing), DecodeVMLDrawing: make(map[string]*decodeVmlDrawing),
VMLDrawing: make(map[string]*vmlDrawing), VMLDrawing: make(map[string]*vmlDrawing),
Relationships: make(map[string]*xlsxRelationships), Relationships: sync.Map{},
CharsetReader: charset.NewReaderLabel, CharsetReader: charset.NewReaderLabel,
} }
} }
@ -277,7 +277,7 @@ func (f *File) addRels(relPath, relType, target, targetMode string) int {
Target: target, Target: target,
TargetMode: targetMode, TargetMode: targetMode,
}) })
f.Relationships[relPath] = rels f.Relationships.Store(relPath, rels)
return rID return rID
} }

View File

@ -975,7 +975,7 @@ func TestGetActiveSheetIndex(t *testing.T) {
func TestRelsWriter(t *testing.T) { func TestRelsWriter(t *testing.T) {
f := NewFile() 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() f.relsWriter()
} }
@ -1231,7 +1231,7 @@ func TestRelsReader(t *testing.T) {
// Test unsupported charset. // Test unsupported charset.
f := NewFile() f := NewFile()
rels := "xl/_rels/workbook.xml.rels" rels := "xl/_rels/workbook.xml.rels"
f.Relationships[rels] = nil f.Relationships.Store(rels, nil)
f.XLSX[rels] = MacintoshCyrillicCharset f.XLSX[rels] = MacintoshCyrillicCharset
f.relsReader(rels) f.relsReader(rels)
} }
@ -1239,7 +1239,7 @@ func TestRelsReader(t *testing.T) {
func TestDeleteSheetFromWorkbookRels(t *testing.T) { func TestDeleteSheetFromWorkbookRels(t *testing.T) {
f := NewFile() f := NewFile()
rels := "xl/_rels/workbook.xml.rels" rels := "xl/_rels/workbook.xml.rels"
f.Relationships[rels] = nil f.Relationships.Store(rels, nil)
assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "") assert.Equal(t, f.deleteSheetFromWorkbookRels("rID"), "")
} }

View File

@ -17,6 +17,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"sync"
) )
// NewFile provides a function to create new file by default template. For // NewFile provides a function to create new file by default template. For
@ -40,13 +41,13 @@ func NewFile() *File {
f.CalcChain = f.calcChainReader() f.CalcChain = f.calcChainReader()
f.Comments = make(map[string]*xlsxComments) f.Comments = make(map[string]*xlsxComments)
f.ContentTypes = f.contentTypesReader() f.ContentTypes = f.contentTypesReader()
f.Drawings = make(map[string]*xlsxWsDr) f.Drawings = sync.Map{}
f.Styles = f.stylesReader() f.Styles = f.stylesReader()
f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing) f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
f.VMLDrawing = make(map[string]*vmlDrawing) f.VMLDrawing = make(map[string]*vmlDrawing)
f.WorkBook = f.workbookReader() f.WorkBook = f.workbookReader()
f.Relationships = make(map[string]*xlsxRelationships) f.Relationships = sync.Map{}
f.Relationships["xl/_rels/workbook.xml.rels"] = f.relsReader("xl/_rels/workbook.xml.rels") 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.Sheet["xl/worksheets/sheet1.xml"], _ = f.workSheetReader("Sheet1")
f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml" f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
f.Theme = f.themeReader() f.Theme = f.themeReader()

View File

@ -189,7 +189,7 @@ func (f *File) deleteSheetRelationships(sheet, rID string) {
sheetRels.Relationships = append(sheetRels.Relationships[:k], sheetRels.Relationships[k+1:]...) 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 // addSheetLegacyDrawing provides a function to add legacy drawing element to
@ -228,11 +228,12 @@ func (f *File) countDrawings() int {
c1++ c1++
} }
} }
for rel := range f.Drawings { f.Drawings.Range(func(rel, value interface{}) bool {
if strings.Contains(rel, "xl/drawings/drawing") { if strings.Contains(rel.(string), "xl/drawings/drawing") {
c2++ c2++
} }
} return true
})
if c1 < c2 { if c1 < c2 {
return c2 return c2
} }
@ -296,7 +297,7 @@ func (f *File) addDrawingPicture(sheet, drawingXML, cell, file string, width, he
FPrintsWithSheet: formatSet.FPrintsWithSheet, FPrintsWithSheet: formatSet.FPrintsWithSheet,
} }
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content f.Drawings.Store(drawingXML, content)
return err 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 // drawingsWriter provides a function to save xl/drawings/drawing%d.xml after
// serialize structure. // serialize structure.
func (f *File) drawingsWriter() { func (f *File) drawingsWriter() {
for path, d := range f.Drawings { f.Drawings.Range(func(path, d interface{}) bool {
if d != nil { if d != nil {
v, _ := xml.Marshal(d) v, _ := xml.Marshal(d.(*xlsxWsDr))
f.saveFileList(path, v) f.saveFileList(path.(string), v)
} }
} return true
})
} }
// drawingResize calculate the height and width after resizing. // drawingResize calculate the height and width after resizing.

View File

@ -436,7 +436,7 @@ func (f *File) addDrawingShape(sheet, drawingXML, cell string, formatSet *format
FPrintsWithSheet: formatSet.Format.FPrintsWithSheet, FPrintsWithSheet: formatSet.Format.FPrintsWithSheet,
} }
content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor) content.TwoCellAnchor = append(content.TwoCellAnchor, &twoCellAnchor)
f.Drawings[drawingXML] = content f.Drawings.Store(drawingXML, content)
return err return err
} }

View File

@ -231,15 +231,16 @@ func (f *File) setWorkbook(name string, sheetID, rid int) {
// relsWriter provides a function to save relationships after // relsWriter provides a function to save relationships after
// serialize structure. // serialize structure.
func (f *File) relsWriter() { func (f *File) relsWriter() {
for path, rel := range f.Relationships { f.Relationships.Range(func(path, rel interface{}) bool {
if rel != nil { if rel != nil {
output, _ := xml.Marshal(rel) output, _ := xml.Marshal(rel.(*xlsxRelationships))
if strings.HasPrefix(path, "xl/worksheets/sheet/rels/sheet") { if strings.HasPrefix(path.(string), "xl/worksheets/sheet/rels/sheet") {
output = f.replaceNameSpaceBytes(path, output) 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. // 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 // 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 // the given sheet index. If the given sheet index is invalid, it will return
// an empty string. // an empty string.
@ -541,7 +526,7 @@ func (f *File) DeleteSheet(name string) {
delete(f.sheetMap, sheetName) delete(f.sheetMap, sheetName)
delete(f.XLSX, sheetXML) delete(f.XLSX, sheetXML)
delete(f.XLSX, rels) delete(f.XLSX, rels)
delete(f.Relationships, rels) f.Relationships.Delete(rels)
delete(f.Sheet, sheetXML) delete(f.Sheet, sheetXML)
delete(f.xmlAttr, sheetXML) delete(f.xmlAttr, sheetXML)
f.SheetCount-- 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. // after deserialization of xl/worksheets/_rels/sheet%d.xml.rels.
func (f *File) relsReader(path string) *xlsxRelationships { func (f *File) relsReader(path string) *xlsxRelationships {
var err error var err error
rels, _ := f.Relationships.Load(path)
if f.Relationships[path] == nil { if rels == nil {
_, ok := f.XLSX[path] _, ok := f.XLSX[path]
if ok { if ok {
c := xlsxRelationships{} c := xlsxRelationships{}
@ -1736,11 +1721,13 @@ func (f *File) relsReader(path string) *xlsxRelationships {
Decode(&c); err != nil && err != io.EOF { Decode(&c); err != nil && err != io.EOF {
log.Printf("xml decode error: %s", err) log.Printf("xml decode error: %s", err)
} }
f.Relationships[path] = &c f.Relationships.Store(path, &c)
} }
} }
if rels, _ = f.Relationships.Load(path); rels != nil {
return f.Relationships[path] return rels.(*xlsxRelationships)
}
return nil
} }
// fillSheetData ensures there are enough rows, and columns in the chosen // fillSheetData ensures there are enough rows, and columns in the chosen