This closes #861, support concurrency get cell picture and remove unused internal function `getSheetNameByID`
This commit is contained in:
parent
5ec61310dc
commit
0e02329bed
10
cell_test.go
10
cell_test.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
16
drawing.go
16
drawing.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>`)},
|
||||||
|
|
10
excelize.go
10
excelize.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
7
file.go
7
file.go
|
@ -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()
|
||||||
|
|
20
picture.go
20
picture.go
|
@ -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.
|
||||||
|
|
2
shape.go
2
shape.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
sheet.go
43
sheet.go
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue