Make the functions `SetSheetRow`, `New Style` and `SetCellStyle` concurrency safety

This commit is contained in:
xuri 2021-07-07 00:57:43 +08:00
parent b7fece5173
commit 90d200a10b
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 44 additions and 2 deletions

20
cell.go
View File

@ -139,7 +139,9 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
if err != nil { if err != nil {
return err return err
} }
ws.Lock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.S = f.prepareCellStyle(ws, col, cellData.S)
ws.Unlock()
var isNum bool var isNum bool
cellData.T, cellData.V, isNum, err = setCellTime(value) cellData.T, cellData.V, isNum, err = setCellTime(value)
@ -155,6 +157,8 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error {
return err return err
} }
// setCellTime prepares cell type and Excel time by given Go time.Time type
// timestamp.
func setCellTime(value time.Time) (t string, b string, isNum bool, err error) { func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
var excelTime float64 var excelTime float64
excelTime, err = timeToExcelTime(value) excelTime, err = timeToExcelTime(value)
@ -170,6 +174,8 @@ func setCellTime(value time.Time) (t string, b string, isNum bool, err error) {
return return
} }
// setCellDuration prepares cell type and value by given Go time.Duration type
// time duration.
func setCellDuration(value time.Duration) (t string, v string) { func setCellDuration(value time.Duration) (t string, v string) {
v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32) v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32)
return return
@ -193,6 +199,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error {
return err return err
} }
// setCellInt prepares cell type and string type cell value by a given
// integer.
func setCellInt(value int) (t string, v string) { func setCellInt(value int) (t string, v string) {
v = strconv.Itoa(value) v = strconv.Itoa(value)
return return
@ -209,11 +217,15 @@ func (f *File) SetCellBool(sheet, axis string, value bool) error {
if err != nil { if err != nil {
return err return err
} }
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = setCellBool(value) cellData.T, cellData.V = setCellBool(value)
return err return err
} }
// setCellBool prepares cell type and string type cell value by a given
// boolean value.
func setCellBool(value bool) (t string, v string) { func setCellBool(value bool) (t string, v string) {
t = "b" t = "b"
if value { if value {
@ -242,11 +254,15 @@ func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int
if err != nil { if err != nil {
return err return err
} }
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = setCellFloat(value, prec, bitSize) cellData.T, cellData.V = setCellFloat(value, prec, bitSize)
return err return err
} }
// setCellFloat prepares cell type and string type cell value by a given
// float value.
func setCellFloat(value float64, prec, bitSize int) (t string, v string) { func setCellFloat(value float64, prec, bitSize int) (t string, v string) {
v = strconv.FormatFloat(value, 'f', prec, bitSize) v = strconv.FormatFloat(value, 'f', prec, bitSize)
return return
@ -334,11 +350,15 @@ func (f *File) SetCellDefault(sheet, axis, value string) error {
if err != nil { if err != nil {
return err return err
} }
ws.Lock()
defer ws.Unlock()
cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.S = f.prepareCellStyle(ws, col, cellData.S)
cellData.T, cellData.V = setCellDefault(value) cellData.T, cellData.V = setCellDefault(value)
return err return err
} }
// setCellDefault prepares cell type and string type cell value by a given
// string.
func setCellDefault(value string) (t string, v string) { func setCellDefault(value string) (t string, v string) {
v = value v = value
return return

View File

@ -25,8 +25,20 @@ func TestConcurrency(t *testing.T) {
// Concurrency set cell value // 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)))
// Concurrency get cell value
_, 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 set rows
assert.NoError(t, f.SetSheetRow("Sheet1", "B6", &[]interface{}{" Hello",
[]byte("World"), 42, int8(1<<8/2 - 1), int16(1<<16/2 - 1), int32(1<<32/2 - 1),
int64(1<<32/2 - 1), float32(42.65418), float64(-42.65418), float32(42), float64(42),
uint(1<<32 - 1), uint8(1<<8 - 1), uint16(1<<16 - 1), uint32(1<<32 - 1),
uint64(1<<32 - 1), true, complex64(5 + 10i)}))
// Concurrency create style
style, err := f.NewStyle(`{"font":{"color":"#1265BE","underline":"single"}}`)
assert.NoError(t, err)
// Concurrency set cell style
assert.NoError(t, f.SetCellStyle("Sheet1", "A3", "A3", style))
// Concurrency add picture // Concurrency add picture
assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"), 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"}`)) `{"x_offset": 10, "y_offset": 10, "hyperlink": "https://github.com/360EntSecGroup-Skylar/excelize", "hyperlink_type": "External", "positioning": "oneCell"}`))
@ -59,6 +71,7 @@ func TestConcurrency(t *testing.T) {
t.Error(err) t.Error(err)
} }
assert.Equal(t, "1", val) assert.Equal(t, "1", val)
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestConcurrency.xlsx")))
} }
func TestCheckCellInArea(t *testing.T) { func TestCheckCellInArea(t *testing.T) {

View File

@ -1777,6 +1777,8 @@ func fillColumns(rowData *xlsxRow, col, row int) {
// makeContiguousColumns make columns in specific rows as contiguous. // makeContiguousColumns make columns in specific rows as contiguous.
func makeContiguousColumns(ws *xlsxWorksheet, fromRow, toRow, colCount int) { func makeContiguousColumns(ws *xlsxWorksheet, fromRow, toRow, colCount int) {
ws.Lock()
defer ws.Unlock()
for ; fromRow < toRow; fromRow++ { for ; fromRow < toRow; fromRow++ {
rowData := &ws.SheetData.Row[fromRow-1] rowData := &ws.SheetData.Row[fromRow-1]
fillColumns(rowData, colCount, fromRow) fillColumns(rowData, colCount, fromRow)

View File

@ -1990,6 +1990,8 @@ func (f *File) NewStyle(style interface{}) (int, error) {
fs.DecimalPlaces = 2 fs.DecimalPlaces = 2
} }
s := f.stylesReader() s := f.stylesReader()
s.Lock()
defer s.Unlock()
// check given style already exist. // check given style already exist.
if cellXfsID = f.getStyleID(s, fs); cellXfsID != -1 { if cellXfsID = f.getStyleID(s, fs); cellXfsID != -1 {
return cellXfsID, err return cellXfsID, err
@ -2693,7 +2695,8 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error {
} }
prepareSheetXML(ws, vcol, vrow) prepareSheetXML(ws, vcol, vrow)
makeContiguousColumns(ws, hrow, vrow, vcol) makeContiguousColumns(ws, hrow, vrow, vcol)
ws.Lock()
defer ws.Unlock()
for r := hrowIdx; r <= vrowIdx; r++ { for r := hrowIdx; r <= vrowIdx; r++ {
for k := hcolIdx; k <= vcolIdx; k++ { for k := hcolIdx; k <= vcolIdx; k++ {
ws.SheetData.Row[r].C[k].S = styleID ws.SheetData.Row[r].C[k].S = styleID

View File

@ -11,10 +11,14 @@
package excelize package excelize
import "encoding/xml" import (
"encoding/xml"
"sync"
)
// xlsxStyleSheet is the root element of the Styles part. // xlsxStyleSheet is the root element of the Styles part.
type xlsxStyleSheet struct { type xlsxStyleSheet struct {
sync.Mutex
XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main styleSheet"` XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main styleSheet"`
NumFmts *xlsxNumFmts `xml:"numFmts,omitempty"` NumFmts *xlsxNumFmts `xml:"numFmts,omitempty"`
Fonts *xlsxFonts `xml:"fonts,omitempty"` Fonts *xlsxFonts `xml:"fonts,omitempty"`