diff --git a/cell.go b/cell.go index 1d08c8a..82e93c5 100644 --- a/cell.go +++ b/cell.go @@ -139,7 +139,9 @@ func (f *File) setCellTimeFunc(sheet, axis string, value time.Time) error { if err != nil { return err } + ws.Lock() cellData.S = f.prepareCellStyle(ws, col, cellData.S) + ws.Unlock() var isNum bool 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 } +// 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) { var excelTime float64 excelTime, err = timeToExcelTime(value) @@ -170,6 +174,8 @@ func setCellTime(value time.Time) (t string, b string, isNum bool, err error) { return } +// setCellDuration prepares cell type and value by given Go time.Duration type +// time duration. func setCellDuration(value time.Duration) (t string, v string) { v = strconv.FormatFloat(value.Seconds()/86400.0, 'f', -1, 32) return @@ -193,6 +199,8 @@ func (f *File) SetCellInt(sheet, axis string, value int) error { return err } +// setCellInt prepares cell type and string type cell value by a given +// integer. func setCellInt(value int) (t string, v string) { v = strconv.Itoa(value) return @@ -209,11 +217,15 @@ func (f *File) SetCellBool(sheet, axis string, value bool) error { if err != nil { return err } + ws.Lock() + defer ws.Unlock() cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.T, cellData.V = setCellBool(value) return err } +// setCellBool prepares cell type and string type cell value by a given +// boolean value. func setCellBool(value bool) (t string, v string) { t = "b" if value { @@ -242,11 +254,15 @@ func (f *File) SetCellFloat(sheet, axis string, value float64, prec, bitSize int if err != nil { return err } + ws.Lock() + defer ws.Unlock() cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.T, cellData.V = setCellFloat(value, prec, bitSize) 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) { v = strconv.FormatFloat(value, 'f', prec, bitSize) return @@ -334,11 +350,15 @@ func (f *File) SetCellDefault(sheet, axis, value string) error { if err != nil { return err } + ws.Lock() + defer ws.Unlock() cellData.S = f.prepareCellStyle(ws, col, cellData.S) cellData.T, cellData.V = setCellDefault(value) return err } +// setCellDefault prepares cell type and string type cell value by a given +// string. func setCellDefault(value string) (t string, v string) { v = value return diff --git a/cell_test.go b/cell_test.go index 91f4804..f11c708 100644 --- a/cell_test.go +++ b/cell_test.go @@ -25,8 +25,20 @@ func TestConcurrency(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))) + // Concurrency get cell value _, err := f.GetCellValue("Sheet1", fmt.Sprintf("A%d", val)) 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 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"}`)) @@ -59,6 +71,7 @@ func TestConcurrency(t *testing.T) { t.Error(err) } assert.Equal(t, "1", val) + assert.NoError(t, f.SaveAs(filepath.Join("test", "TestConcurrency.xlsx"))) } func TestCheckCellInArea(t *testing.T) { diff --git a/sheet.go b/sheet.go index 3a55540..8d3d457 100644 --- a/sheet.go +++ b/sheet.go @@ -1777,6 +1777,8 @@ func fillColumns(rowData *xlsxRow, col, row int) { // makeContiguousColumns make columns in specific rows as contiguous. func makeContiguousColumns(ws *xlsxWorksheet, fromRow, toRow, colCount int) { + ws.Lock() + defer ws.Unlock() for ; fromRow < toRow; fromRow++ { rowData := &ws.SheetData.Row[fromRow-1] fillColumns(rowData, colCount, fromRow) diff --git a/styles.go b/styles.go index 235746c..5b9b200 100644 --- a/styles.go +++ b/styles.go @@ -1990,6 +1990,8 @@ func (f *File) NewStyle(style interface{}) (int, error) { fs.DecimalPlaces = 2 } s := f.stylesReader() + s.Lock() + defer s.Unlock() // check given style already exist. if cellXfsID = f.getStyleID(s, fs); cellXfsID != -1 { return cellXfsID, err @@ -2693,7 +2695,8 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error { } prepareSheetXML(ws, vcol, vrow) makeContiguousColumns(ws, hrow, vrow, vcol) - + ws.Lock() + defer ws.Unlock() for r := hrowIdx; r <= vrowIdx; r++ { for k := hcolIdx; k <= vcolIdx; k++ { ws.SheetData.Row[r].C[k].S = styleID diff --git a/xmlStyles.go b/xmlStyles.go index 92e4e6a..afdc170 100644 --- a/xmlStyles.go +++ b/xmlStyles.go @@ -11,10 +11,14 @@ package excelize -import "encoding/xml" +import ( + "encoding/xml" + "sync" +) // xlsxStyleSheet is the root element of the Styles part. type xlsxStyleSheet struct { + sync.Mutex XMLName xml.Name `xml:"http://schemas.openxmlformats.org/spreadsheetml/2006/main styleSheet"` NumFmts *xlsxNumFmts `xml:"numFmts,omitempty"` Fonts *xlsxFonts `xml:"fonts,omitempty"`