forked from p30928647/excelize
- Close spreadsheet and row's iterator required - New options `WorksheetUnzipMemLimit` have been added - Improve streaming reading performance, memory usage decrease about 93.7%
This commit is contained in:
parent
2add938798
commit
790c363cce
|
@ -5,6 +5,7 @@ test/Test*.xlsm
|
|||
test/BadEncrypt.xlsx
|
||||
test/BadWorkbook.SaveAsEmptyStruct.xlsx
|
||||
test/*.png
|
||||
test/excelize-*
|
||||
*.out
|
||||
*.test
|
||||
.idea
|
||||
|
|
|
@ -96,6 +96,10 @@ func main() {
|
|||
}
|
||||
fmt.Println()
|
||||
}
|
||||
// Close the spreadsheet.
|
||||
if err = f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -203,6 +207,10 @@ func main() {
|
|||
if err = f.Save(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Close the spreadsheet.
|
||||
if err = f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
10
README_zh.md
10
README_zh.md
|
@ -96,6 +96,10 @@ func main() {
|
|||
}
|
||||
fmt.Println()
|
||||
}
|
||||
// 关闭工作簿
|
||||
if err = f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -199,10 +203,14 @@ func main() {
|
|||
}`); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 保存文件
|
||||
// 保存工作簿
|
||||
if err = f.Save(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// 关闭工作簿
|
||||
if err = f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ func TestConcurrency(t *testing.T) {
|
|||
}
|
||||
assert.Equal(t, "1", val)
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestConcurrency.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestCheckCellInArea(t *testing.T) {
|
||||
|
@ -325,6 +326,7 @@ func TestOverflowNumericCell(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
// GOARCH=amd64 - all ok; GOARCH=386 - actual: "-2147483648"
|
||||
assert.Equal(t, "8595602512225", val, "A1 should be 8595602512225")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetCellFormula(t *testing.T) {
|
||||
|
@ -340,6 +342,7 @@ func TestSetCellFormula(t *testing.T) {
|
|||
assert.EqualError(t, f.SetCellFormula("Sheet1", "C", "SUM(Sheet2!D2,Sheet2!D9)"), `cannot convert cell "C" to coordinates: invalid cell name "C"`)
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula1.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f, err = OpenFile(filepath.Join("test", "CalcChain.xlsx"))
|
||||
if !assert.NoError(t, err) {
|
||||
|
@ -351,6 +354,7 @@ func TestSetCellFormula(t *testing.T) {
|
|||
// Test remove all cell formula.
|
||||
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", ""))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellFormula3.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test set shared formula for the cells.
|
||||
f = NewFile()
|
||||
|
|
|
@ -206,6 +206,7 @@ func TestAddChart(t *testing.T) {
|
|||
assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"col","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"name":"Sheet1!$A$31","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$31:$D$31"},{"name":"Sheet1!$A$32","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$32:$D$32"},{"name":"Sheet1!$A$33","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$33:$D$33"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"2D Column Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`, ""), "unexpected end of JSON input")
|
||||
// Test add combo chart with unsupported chart type
|
||||
assert.EqualError(t, f.AddChart("Sheet2", "BD64", `{"type":"barOfPie","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`, `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$A$30:$D$37","values":"Sheet1!$B$30:$B$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bar of Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`), "unsupported chart type unknown")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestAddChartSheet(t *testing.T) {
|
||||
|
@ -254,6 +255,7 @@ func TestDeleteChart(t *testing.T) {
|
|||
assert.EqualError(t, f.DeleteChart("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`)
|
||||
// Test delete chart on no chart worksheet.
|
||||
assert.NoError(t, NewFile().DeleteChart("Sheet1", "A1"))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestChartWithLogarithmicBase(t *testing.T) {
|
||||
|
|
2
col.go
2
col.go
|
@ -209,7 +209,7 @@ func (f *File) Cols(sheet string) (*Cols, error) {
|
|||
f.saveFileList(name, f.replaceNameSpaceBytes(name, output))
|
||||
}
|
||||
var colIterator columnXMLIterator
|
||||
colIterator.cols.sheetXML = f.readXML(name)
|
||||
colIterator.cols.sheetXML = f.readBytes(name)
|
||||
decoder := f.xmlNewDecoder(bytes.NewReader(colIterator.cols.sheetXML))
|
||||
for {
|
||||
token, _ := decoder.Token()
|
||||
|
|
|
@ -39,6 +39,7 @@ func TestCols(t *testing.T) {
|
|||
if !assert.Equal(t, collectedRows, returnedColumns) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
cells := []string{"C2", "C3", "C4"}
|
||||
|
@ -75,6 +76,7 @@ func TestColumnsIterator(t *testing.T) {
|
|||
require.True(t, colCount <= expectedNumCol, "colCount is greater than expected")
|
||||
}
|
||||
assert.Equal(t, expectedNumCol, colCount)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
cells := []string{"C2", "C3", "C4", "D2", "D3", "D4"}
|
||||
|
@ -99,6 +101,7 @@ func TestColsError(t *testing.T) {
|
|||
}
|
||||
_, err = f.Cols("SheetN")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestGetColsError(t *testing.T) {
|
||||
|
@ -108,6 +111,7 @@ func TestGetColsError(t *testing.T) {
|
|||
}
|
||||
_, err = f.GetCols("SheetN")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
f.Sheet.Delete("xl/worksheets/sheet1.xml")
|
||||
|
@ -283,6 +287,7 @@ func TestOutlineLevel(t *testing.T) {
|
|||
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.SetColOutlineLevel("Sheet2", "B", 2))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetColStyle(t *testing.T) {
|
||||
|
|
|
@ -22,6 +22,7 @@ func TestEncrypt(t *testing.T) {
|
|||
f, err := OpenFile(filepath.Join("test", "encryptSHA1.xlsx"), Options{Password: "password"})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualError(t, f.SaveAs(filepath.Join("test", "BadEncrypt.xlsx"), Options{Password: "password"}), "not support encryption currently")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestEncryptionMechanism(t *testing.T) {
|
||||
|
|
|
@ -44,6 +44,7 @@ func TestSetDocProps(t *testing.T) {
|
|||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetDocProps.xlsx")))
|
||||
f.Pkg.Store("docProps/core.xml", nil)
|
||||
assert.NoError(t, f.SetDocProps(&DocProperties{}))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
|
@ -62,6 +63,7 @@ func TestGetDocProps(t *testing.T) {
|
|||
f.Pkg.Store("docProps/core.xml", nil)
|
||||
_, err = f.GetDocProps()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test unsupported charset
|
||||
f = NewFile()
|
||||
|
|
|
@ -131,4 +131,7 @@ var (
|
|||
// ErrCellCharsLength defined the error message for receiving a cell
|
||||
// characters length that exceeds the limit.
|
||||
ErrCellCharsLength = fmt.Errorf("cell value must be 0-%d characters", TotalCellChars)
|
||||
// ErrOptionsUnzipSizeLimit defined the error message for receiving
|
||||
// invalid UnzipSizeLimit and WorksheetUnzipMemLimit.
|
||||
ErrOptionsUnzipSizeLimit = errors.New("the value of UnzipSizeLimit should be greater than or equal to WorksheetUnzipMemLimit")
|
||||
)
|
||||
|
|
53
excelize.go
53
excelize.go
|
@ -37,6 +37,7 @@ type File struct {
|
|||
checked map[string]bool
|
||||
sheetMap map[string]string
|
||||
streams map[string]*StreamWriter
|
||||
tempFiles sync.Map
|
||||
CalcChain *xlsxCalcChain
|
||||
Comments map[string]*xlsxComments
|
||||
ContentTypes *xlsxTypes
|
||||
|
@ -58,13 +59,26 @@ type File struct {
|
|||
|
||||
type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, err error)
|
||||
|
||||
// Options define the options for open and reading spreadsheet. RawCellValue
|
||||
// specify if apply the number format for the cell value or get the raw
|
||||
// value.
|
||||
// Options define the options for open and reading spreadsheet.
|
||||
//
|
||||
// Password specifies the password of the spreadsheet in plain text.
|
||||
//
|
||||
// RawCellValue specifies if apply the number format for the cell value or get
|
||||
// the raw value.
|
||||
//
|
||||
// UnzipSizeLimit specifies the unzip size limit in bytes on open the
|
||||
// spreadsheet, this value should be greater than or equal to
|
||||
// WorksheetUnzipMemLimit, the default size limit is 16GB.
|
||||
//
|
||||
// WorksheetUnzipMemLimit specifies the memory limit on unzipping worksheet in
|
||||
// bytes, worksheet XML will be extracted to system temporary directory when
|
||||
// the file size is over this value, this value should be less than or equal
|
||||
// to UnzipSizeLimit, the default value is 16MB.
|
||||
type Options struct {
|
||||
Password string
|
||||
RawCellValue bool
|
||||
UnzipSizeLimit int64
|
||||
Password string
|
||||
RawCellValue bool
|
||||
UnzipSizeLimit int64
|
||||
WorksheetUnzipMemLimit int64
|
||||
}
|
||||
|
||||
// OpenFile take the name of an spreadsheet file and returns a populated
|
||||
|
@ -78,10 +92,8 @@ type Options struct {
|
|||
//
|
||||
// Note that the excelize just support decrypt and not support encrypt
|
||||
// currently, the spreadsheet saved by Save and SaveAs will be without
|
||||
// password unprotected.
|
||||
//
|
||||
// UnzipSizeLimit specified the unzip size limit in bytes on open the
|
||||
// spreadsheet, the default size limit is 16GB.
|
||||
// password unprotected. Close the file by Close after opening the
|
||||
// spreadsheet.
|
||||
func OpenFile(filename string, opt ...Options) (*File, error) {
|
||||
file, err := os.Open(filepath.Clean(filename))
|
||||
if err != nil {
|
||||
|
@ -99,10 +111,11 @@ func OpenFile(filename string, opt ...Options) (*File, error) {
|
|||
// newFile is object builder
|
||||
func newFile() *File {
|
||||
return &File{
|
||||
options: &Options{UnzipSizeLimit: UnzipSizeLimit},
|
||||
options: &Options{UnzipSizeLimit: UnzipSizeLimit, WorksheetUnzipMemLimit: StreamChunkSize},
|
||||
xmlAttr: make(map[string][]xml.Attr),
|
||||
checked: make(map[string]bool),
|
||||
sheetMap: make(map[string]string),
|
||||
tempFiles: sync.Map{},
|
||||
Comments: make(map[string]*xlsxComments),
|
||||
Drawings: sync.Map{},
|
||||
sharedStringsMap: make(map[string]int),
|
||||
|
@ -125,6 +138,18 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
|
|||
f.options = parseOptions(opt...)
|
||||
if f.options.UnzipSizeLimit == 0 {
|
||||
f.options.UnzipSizeLimit = UnzipSizeLimit
|
||||
if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
|
||||
f.options.UnzipSizeLimit = f.options.WorksheetUnzipMemLimit
|
||||
}
|
||||
}
|
||||
if f.options.WorksheetUnzipMemLimit == 0 {
|
||||
f.options.WorksheetUnzipMemLimit = StreamChunkSize
|
||||
if f.options.UnzipSizeLimit < f.options.WorksheetUnzipMemLimit {
|
||||
f.options.WorksheetUnzipMemLimit = f.options.UnzipSizeLimit
|
||||
}
|
||||
}
|
||||
if f.options.WorksheetUnzipMemLimit > f.options.UnzipSizeLimit {
|
||||
return nil, ErrOptionsUnzipSizeLimit
|
||||
}
|
||||
if bytes.Contains(b, oleIdentifier) {
|
||||
b, err = Decrypt(b, f.options)
|
||||
|
@ -136,7 +161,7 @@ func OpenReader(r io.Reader, opt ...Options) (*File, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, sheetCount, err := ReadZipReader(zr, f.options)
|
||||
file, sheetCount, err := f.ReadZipReader(zr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -210,10 +235,10 @@ func (f *File) workSheetReader(sheet string) (ws *xlsxWorksheet, err error) {
|
|||
}
|
||||
ws = new(xlsxWorksheet)
|
||||
if _, ok := f.xmlAttr[name]; !ok {
|
||||
d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name))))
|
||||
d := f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name))))
|
||||
f.xmlAttr[name] = append(f.xmlAttr[name], getRootElement(d)...)
|
||||
}
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readXML(name)))).
|
||||
if err = f.xmlNewDecoder(bytes.NewReader(namespaceStrictToTransitional(f.readBytes(name)))).
|
||||
Decode(ws); err != nil && err != io.EOF {
|
||||
err = fmt.Errorf("xml decode error: %s", err)
|
||||
return
|
||||
|
|
|
@ -167,6 +167,7 @@ func TestOpenFile(t *testing.T) {
|
|||
}
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestOpenFile.xlsx")))
|
||||
assert.EqualError(t, f.SaveAs(filepath.Join("test", strings.Repeat("c", 199), ".xlsx")), ErrMaxFileNameLength.Error())
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSaveFile(t *testing.T) {
|
||||
|
@ -175,11 +176,13 @@ func TestSaveFile(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSaveFile.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
f, err = OpenFile(filepath.Join("test", "TestSaveFile.xlsx"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, f.Save())
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSaveAsWrongPath(t *testing.T) {
|
||||
|
@ -187,6 +190,7 @@ func TestSaveAsWrongPath(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
// Test write file to not exist directory.
|
||||
assert.EqualError(t, f.SaveAs(""), "open .: is a directory")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestCharsetTranscoder(t *testing.T) {
|
||||
|
@ -197,7 +201,7 @@ func TestCharsetTranscoder(t *testing.T) {
|
|||
func TestOpenReader(t *testing.T) {
|
||||
_, err := OpenReader(strings.NewReader(""))
|
||||
assert.EqualError(t, err, "zip: not a valid zip file")
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password"})
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{Password: "password", WorksheetUnzipMemLimit: UnzipSizeLimit + 1})
|
||||
assert.EqualError(t, err, "decrypted file failed")
|
||||
|
||||
// Test open spreadsheet with unzip size limit.
|
||||
|
@ -210,6 +214,7 @@ func TestOpenReader(t *testing.T) {
|
|||
val, err := f.GetCellValue("Sheet1", "A1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "SECRET", val)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test open password protected spreadsheet created by LibreOffice 7.0.0.3.
|
||||
f, err = OpenFile(filepath.Join("test", "encryptAES.xlsx"), Options{Password: "password"})
|
||||
|
@ -217,6 +222,11 @@ func TestOpenReader(t *testing.T) {
|
|||
val, err = f.GetCellValue("Sheet1", "A1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "SECRET", val)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test open spreadsheet with invalid optioins.
|
||||
_, err = OpenReader(bytes.NewReader(oleIdentifier), Options{UnzipSizeLimit: 1, WorksheetUnzipMemLimit: 2})
|
||||
assert.EqualError(t, err, ErrOptionsUnzipSizeLimit.Error())
|
||||
|
||||
// Test unexpected EOF.
|
||||
var b bytes.Buffer
|
||||
|
@ -266,6 +276,7 @@ func TestBrokenFile(t *testing.T) {
|
|||
f3.GetActiveSheetIndex()
|
||||
f3.SetActiveSheet(1)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f3.Close())
|
||||
})
|
||||
|
||||
t.Run("OpenNotExistsFile", func(t *testing.T) {
|
||||
|
@ -340,6 +351,7 @@ func TestSetCellHyperLink(t *testing.T) {
|
|||
assert.EqualError(t, f.SetCellHyperLink("Sheet2", "", "Sheet1!D60", "Location"), `invalid cell name ""`)
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetCellHyperLink.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
_, err = f.workSheetReader("Sheet1")
|
||||
|
@ -377,6 +389,7 @@ func TestGetCellHyperLink(t *testing.T) {
|
|||
link, target, err = f.GetCellHyperLink("Sheet3", "H3")
|
||||
assert.EqualError(t, err, "sheet Sheet3 is not exist")
|
||||
t.Log(link, target)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
_, err = f.workSheetReader("Sheet1")
|
||||
|
@ -398,7 +411,6 @@ func TestGetCellHyperLink(t *testing.T) {
|
|||
assert.EqualError(t, err, `cannot convert cell "A" to coordinates: invalid cell name "A"`)
|
||||
assert.Equal(t, link, false)
|
||||
assert.Equal(t, target, "")
|
||||
|
||||
}
|
||||
|
||||
func TestSetSheetBackground(t *testing.T) {
|
||||
|
@ -418,6 +430,7 @@ func TestSetSheetBackground(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetBackground.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetSheetBackgroundErrors(t *testing.T) {
|
||||
|
@ -433,6 +446,7 @@ func TestSetSheetBackgroundErrors(t *testing.T) {
|
|||
|
||||
err = f.SetSheetBackground("Sheet2", filepath.Join("test", "Book1.xlsx"))
|
||||
assert.EqualError(t, err, ErrImgExt.Error())
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
// TestWriteArrayFormula tests the extended options of SetCellFormula by writing an array function
|
||||
|
@ -1052,6 +1066,7 @@ func TestConditionalFormat(t *testing.T) {
|
|||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestConditionalFormatError(t *testing.T) {
|
||||
|
@ -1082,6 +1097,7 @@ func TestSharedStrings(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, "Test Weight (Kgs)", rows[0][0])
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetSheetRow(t *testing.T) {
|
||||
|
@ -1098,6 +1114,7 @@ func TestSetSheetRow(t *testing.T) {
|
|||
assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", []interface{}{}), ErrParameterInvalid.Error())
|
||||
assert.EqualError(t, f.SetSheetRow("Sheet1", "B27", &f), ErrParameterInvalid.Error())
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestSetSheetRow.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestHSL(t *testing.T) {
|
||||
|
@ -1155,6 +1172,7 @@ func TestUnprotectSheet(t *testing.T) {
|
|||
|
||||
assert.NoError(t, f.UnprotectSheet("Sheet1"))
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnprotectSheet.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetDefaultTimeStyle(t *testing.T) {
|
||||
|
@ -1323,7 +1341,11 @@ func fillCells(f *File, sheet string, colCount, rowCount int) {
|
|||
|
||||
func BenchmarkOpenFile(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := OpenFile(filepath.Join("test", "Book1.xlsx")); err != nil {
|
||||
f, err := OpenFile(filepath.Join("test", "Book1.xlsx"))
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
|
12
file.go
12
file.go
|
@ -82,6 +82,18 @@ func (f *File) SaveAs(name string, opt ...Options) error {
|
|||
return f.Write(file)
|
||||
}
|
||||
|
||||
// Close closes and cleanup the open temporary file for the spreadsheet.
|
||||
func (f *File) Close() error {
|
||||
var err error
|
||||
f.tempFiles.Range(func(k, v interface{}) bool {
|
||||
if err = os.Remove(v.(string)); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Write provides a function to write to an io.Writer.
|
||||
func (f *File) Write(w io.Writer) error {
|
||||
_, err := f.WriteTo(w)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkWrite(b *testing.B) {
|
||||
|
@ -62,3 +63,9 @@ func TestWriteTo(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClose(t *testing.T) {
|
||||
f := NewFile()
|
||||
f.tempFiles.Store("/d/", "/d/")
|
||||
require.Error(t, f.Close())
|
||||
}
|
||||
|
|
71
lib.go
71
lib.go
|
@ -18,14 +18,15 @@ import (
|
|||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadZipReader can be used to read the spreadsheet in memory without touching the
|
||||
// filesystem.
|
||||
func ReadZipReader(r *zip.Reader, o *Options) (map[string][]byte, int, error) {
|
||||
// ReadZipReader extract spreadsheet with given options.
|
||||
func (f *File) ReadZipReader(r *zip.Reader) (map[string][]byte, int, error) {
|
||||
var (
|
||||
err error
|
||||
docPart = map[string]string{
|
||||
|
@ -37,25 +38,49 @@ func ReadZipReader(r *zip.Reader, o *Options) (map[string][]byte, int, error) {
|
|||
unzipSize int64
|
||||
)
|
||||
for _, v := range r.File {
|
||||
unzipSize += v.FileInfo().Size()
|
||||
if unzipSize > o.UnzipSizeLimit {
|
||||
return fileList, worksheets, newUnzipSizeLimitError(o.UnzipSizeLimit)
|
||||
fileSize := v.FileInfo().Size()
|
||||
unzipSize += fileSize
|
||||
if unzipSize > f.options.UnzipSizeLimit {
|
||||
return fileList, worksheets, newUnzipSizeLimitError(f.options.UnzipSizeLimit)
|
||||
}
|
||||
fileName := strings.Replace(v.Name, "\\", "/", -1)
|
||||
if partName, ok := docPart[strings.ToLower(fileName)]; ok {
|
||||
fileName = partName
|
||||
}
|
||||
if fileList[fileName], err = readFile(v); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if strings.HasPrefix(fileName, "xl/worksheets/sheet") {
|
||||
worksheets++
|
||||
if fileSize > f.options.WorksheetUnzipMemLimit && !v.FileInfo().IsDir() {
|
||||
if tempFile, err := f.unzipToTemp(v); err == nil {
|
||||
f.tempFiles.Store(fileName, tempFile)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if fileList[fileName], err = readFile(v); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
return fileList, worksheets, nil
|
||||
}
|
||||
|
||||
// readXML provides a function to read XML content as string.
|
||||
// unzipToTemp unzip the zip entity to the system temporary directory and
|
||||
// returned the unzipped file path.
|
||||
func (f *File) unzipToTemp(zipFile *zip.File) (string, error) {
|
||||
tmp, err := ioutil.TempFile(os.TempDir(), "excelize-")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rc, err := zipFile.Open()
|
||||
if err != nil {
|
||||
return tmp.Name(), err
|
||||
}
|
||||
_, err = io.Copy(tmp, rc)
|
||||
rc.Close()
|
||||
tmp.Close()
|
||||
return tmp.Name(), err
|
||||
}
|
||||
|
||||
// readXML provides a function to read XML content as bytes.
|
||||
func (f *File) readXML(name string) []byte {
|
||||
if content, _ := f.Pkg.Load(name); content != nil {
|
||||
return content.([]byte)
|
||||
|
@ -66,6 +91,32 @@ func (f *File) readXML(name string) []byte {
|
|||
return []byte{}
|
||||
}
|
||||
|
||||
// readBytes read file as bytes by given path.
|
||||
func (f *File) readBytes(name string) []byte {
|
||||
content := f.readXML(name)
|
||||
if len(content) != 0 {
|
||||
return content
|
||||
}
|
||||
file, err := f.readTemp(name)
|
||||
if err != nil {
|
||||
return content
|
||||
}
|
||||
content, _ = ioutil.ReadAll(file)
|
||||
f.Pkg.Store(name, content)
|
||||
file.Close()
|
||||
return content
|
||||
}
|
||||
|
||||
// readTemp read file from system temporary directory by given path.
|
||||
func (f *File) readTemp(name string) (file *os.File, err error) {
|
||||
path, ok := f.tempFiles.Load(name)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
file, err = os.Open(path.(string))
|
||||
return
|
||||
}
|
||||
|
||||
// saveFileList provides a function to update given file content in file list
|
||||
// of spreadsheet.
|
||||
func (f *File) saveFileList(name string, content []byte) {
|
||||
|
|
43
lib_test.go
43
lib_test.go
|
@ -1,13 +1,18 @@
|
|||
package excelize
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var validColumns = []struct {
|
||||
|
@ -296,3 +301,41 @@ func TestBstrMarshal(t *testing.T) {
|
|||
assert.Equal(t, expected, bstrMarshal(bstr))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBytes(t *testing.T) {
|
||||
f := &File{tempFiles: sync.Map{}}
|
||||
sheet := "xl/worksheets/sheet1.xml"
|
||||
f.tempFiles.Store(sheet, "/d/")
|
||||
assert.Equal(t, []byte{}, f.readBytes(sheet))
|
||||
}
|
||||
|
||||
func TestUnzipToTemp(t *testing.T) {
|
||||
os.Setenv("TMPDIR", "test")
|
||||
defer os.Unsetenv("TMPDIR")
|
||||
assert.NoError(t, os.Chmod(os.TempDir(), 0444))
|
||||
f := NewFile()
|
||||
data := []byte("PK\x03\x040000000PK\x01\x0200000" +
|
||||
"0000000000000000000\x00" +
|
||||
"\x00\x00\x00\x00\x00000000000000PK\x01" +
|
||||
"\x020000000000000000000" +
|
||||
"00000\v\x00\x00\x00\x00\x00000000000" +
|
||||
"00000000000000PK\x01\x0200" +
|
||||
"00000000000000000000" +
|
||||
"00\v\x00\x00\x00\x00\x00000000000000" +
|
||||
"00000000000PK\x01\x020000<" +
|
||||
"0\x00\x0000000000000000\v\x00\v" +
|
||||
"\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
|
||||
"00000000PK\x01\x0200000000" +
|
||||
"0000000000000000\v\x00\x00\x00" +
|
||||
"\x00\x0000PK\x05\x06000000\x05\x000000" +
|
||||
"\v\x00\x00\x00\x00\x00")
|
||||
z, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = f.unzipToTemp(z.File[0])
|
||||
require.Error(t, err)
|
||||
assert.NoError(t, os.Chmod(os.TempDir(), 0755))
|
||||
|
||||
_, err = f.unzipToTemp(z.File[0])
|
||||
assert.EqualError(t, err, "EOF")
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ func TestMergeCell(t *testing.T) {
|
|||
assert.EqualError(t, f.MergeCell("SheetN", "N10", "O11"), "sheet SheetN is not exist")
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestMergeCell.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
|
||||
|
@ -93,6 +94,7 @@ func TestMergeCellOverlap(t *testing.T) {
|
|||
assert.Equal(t, "A1", mc[0].GetStartAxis())
|
||||
assert.Equal(t, "D3", mc[0].GetEndAxis())
|
||||
assert.Equal(t, "", mc[0].GetCellValue())
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestGetMergeCells(t *testing.T) {
|
||||
|
@ -139,6 +141,7 @@ func TestGetMergeCells(t *testing.T) {
|
|||
// Test get merged cells on not exists worksheet.
|
||||
_, err = f.GetMergeCells("SheetN")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestUnmergeCell(t *testing.T) {
|
||||
|
@ -162,6 +165,7 @@ func TestUnmergeCell(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestUnmergeCell.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
assert.NoError(t, f.MergeCell("Sheet1", "A2", "B3"))
|
||||
|
|
|
@ -497,6 +497,9 @@ func (f *File) getSheetRelationshipsTargetByID(sheet, rID string) string {
|
|||
// if err := ioutil.WriteFile(file, raw, 0644); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
// if err = f.Close(); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
func (f *File) GetPicture(sheet, cell string) (string, []byte, error) {
|
||||
col, row, err := CellNameToCoordinates(cell)
|
||||
|
|
|
@ -68,6 +68,7 @@ func TestAddPicture(t *testing.T) {
|
|||
|
||||
// Test write file to given path.
|
||||
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddPicture.xlsx")))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestAddPictureErrors(t *testing.T) {
|
||||
|
@ -90,6 +91,7 @@ func TestAddPictureErrors(t *testing.T) {
|
|||
// Test add picture to worksheet with invalid file data.
|
||||
err = f.AddPictureFromBytes("Sheet1", "G21", "", "Excel Logo", ".jpg", make([]byte, 1))
|
||||
assert.EqualError(t, err, "image: unknown format")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestGetPicture(t *testing.T) {
|
||||
|
@ -137,7 +139,6 @@ func TestGetPicture(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
if !assert.NotEmpty(t, filepath.Join("test", file)) || !assert.NotEmpty(t, raw) ||
|
||||
!assert.NoError(t, ioutil.WriteFile(filepath.Join("test", file), raw, 0644)) {
|
||||
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
|
@ -146,6 +147,7 @@ func TestGetPicture(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Empty(t, file)
|
||||
assert.Empty(t, raw)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test get picture from none drawing worksheet.
|
||||
f = NewFile()
|
||||
|
@ -194,6 +196,7 @@ func TestDeletePicture(t *testing.T) {
|
|||
assert.EqualError(t, f.DeletePicture("SheetN", "A1"), "sheet SheetN is not exist")
|
||||
// Test delete picture with invalid coordinates.
|
||||
assert.EqualError(t, f.DeletePicture("Sheet1", ""), `cannot convert cell "" to coordinates: invalid cell name ""`)
|
||||
assert.NoError(t, f.Close())
|
||||
// Test delete picture on no chart worksheet.
|
||||
assert.NoError(t, NewFile().DeletePicture("Sheet1", "A1"))
|
||||
}
|
||||
|
|
43
rows.go
43
rows.go
|
@ -18,6 +18,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/mohae/deepcopy"
|
||||
|
@ -60,7 +61,7 @@ func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error) {
|
|||
max = cur
|
||||
}
|
||||
}
|
||||
return results[:max], nil
|
||||
return results[:max], rows.Close()
|
||||
}
|
||||
|
||||
// Rows defines an iterator to a sheet.
|
||||
|
@ -70,6 +71,7 @@ type Rows struct {
|
|||
rawCellValue bool
|
||||
sheet string
|
||||
f *File
|
||||
tempFile *os.File
|
||||
decoder *xml.Decoder
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,15 @@ func (rows *Rows) Error() error {
|
|||
return rows.err
|
||||
}
|
||||
|
||||
// Close closes the open worksheet XML file in the system temporary
|
||||
// directory.
|
||||
func (rows *Rows) Close() error {
|
||||
if rows.tempFile != nil {
|
||||
return rows.tempFile.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Columns return the current row's column values.
|
||||
func (rows *Rows) Columns(opts ...Options) ([]string, error) {
|
||||
var rowIterator rowXMLIterator
|
||||
|
@ -196,6 +207,9 @@ func rowXMLHandler(rowIterator *rowXMLIterator, xmlElement *xml.StartElement, ra
|
|||
// }
|
||||
// fmt.Println()
|
||||
// }
|
||||
// if err = rows.Close(); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
func (f *File) Rows(sheet string) (*Rows, error) {
|
||||
name, ok := f.sheetMap[trimSheetName(sheet)]
|
||||
|
@ -215,8 +229,13 @@ func (f *File) Rows(sheet string) (*Rows, error) {
|
|||
inElement string
|
||||
row int
|
||||
rows Rows
|
||||
needClose bool
|
||||
decoder *xml.Decoder
|
||||
tempFile *os.File
|
||||
)
|
||||
decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
||||
if needClose, decoder, tempFile, err = f.sheetDecoder(name); needClose && err == nil {
|
||||
defer tempFile.Close()
|
||||
}
|
||||
for {
|
||||
token, _ := decoder.Token()
|
||||
if token == nil {
|
||||
|
@ -241,15 +260,29 @@ func (f *File) Rows(sheet string) (*Rows, error) {
|
|||
if xmlElement.Name.Local == "sheetData" {
|
||||
rows.f = f
|
||||
rows.sheet = name
|
||||
rows.decoder = f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
||||
return &rows, nil
|
||||
_, rows.decoder, rows.tempFile, err = f.sheetDecoder(name)
|
||||
return &rows, err
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return &rows, nil
|
||||
}
|
||||
|
||||
// sheetDecoder creates XML decoder by given path in the zip from memory data
|
||||
// or system temporary file.
|
||||
func (f *File) sheetDecoder(name string) (bool, *xml.Decoder, *os.File, error) {
|
||||
var (
|
||||
content []byte
|
||||
err error
|
||||
tempFile *os.File
|
||||
)
|
||||
if content = f.readXML(name); len(content) > 0 {
|
||||
return false, f.xmlNewDecoder(bytes.NewReader(content)), tempFile, err
|
||||
}
|
||||
tempFile, err = f.readTemp(name)
|
||||
return true, f.xmlNewDecoder(tempFile), tempFile, err
|
||||
}
|
||||
|
||||
// SetRowHeight provides a function to set the height of a single row. For
|
||||
// example, set the height of the first row in Sheet1:
|
||||
//
|
||||
|
|
25
rows_test.go
25
rows_test.go
|
@ -32,6 +32,7 @@ func TestRows(t *testing.T) {
|
|||
if !assert.NoError(t, rows.Error()) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, rows.Close())
|
||||
|
||||
returnedRows, err := f.GetRows(sheet2)
|
||||
assert.NoError(t, err)
|
||||
|
@ -41,6 +42,7 @@ func TestRows(t *testing.T) {
|
|||
if !assert.Equal(t, collectedRows, returnedRows) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
f = NewFile()
|
||||
f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(`<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>1</v></c></row><row r="A"><c r="2" t="str"><v>B</v></c></row></sheetData></worksheet>`))
|
||||
|
@ -52,6 +54,14 @@ func TestRows(t *testing.T) {
|
|||
f.Pkg.Store("xl/worksheets/sheet1.xml", nil)
|
||||
_, err = f.Rows("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test reload the file to memory from system temporary directory.
|
||||
f, err = OpenFile(filepath.Join("test", "Book1.xlsx"), Options{WorksheetUnzipMemLimit: 1024})
|
||||
assert.NoError(t, err)
|
||||
value, err := f.GetCellValue("Sheet1", "A19")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Total:", value)
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestRowsIterator(t *testing.T) {
|
||||
|
@ -70,6 +80,8 @@ func TestRowsIterator(t *testing.T) {
|
|||
require.True(t, rowCount <= expectedNumRow, "rowCount is greater than expected")
|
||||
}
|
||||
assert.Equal(t, expectedNumRow, rowCount)
|
||||
assert.NoError(t, rows.Close())
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Valued cell sparse distribution test
|
||||
f = NewFile()
|
||||
|
@ -94,6 +106,7 @@ func TestRowsError(t *testing.T) {
|
|||
}
|
||||
_, err = f.Rows("SheetN")
|
||||
assert.EqualError(t, err, "sheet SheetN is not exist")
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestRowHeight(t *testing.T) {
|
||||
|
@ -871,6 +884,11 @@ func TestGetValueFromNumber(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRoundPrecision(t *testing.T) {
|
||||
_, err := roundPrecision("")
|
||||
assert.EqualError(t, err, "strconv.ParseFloat: parsing \"\": invalid syntax")
|
||||
}
|
||||
|
||||
func TestErrSheetNotExistError(t *testing.T) {
|
||||
err := ErrSheetNotExist{SheetName: "Sheet1"}
|
||||
assert.EqualValues(t, err.Error(), "sheet Sheet1 is not exist")
|
||||
|
@ -920,6 +938,7 @@ func TestNumberFormats(t *testing.T) {
|
|||
cells = append(cells, col)
|
||||
}
|
||||
assert.Equal(t, []string{"", "200", "450", "200", "510", "315", "127", "89", "348", "53", "37"}, cells[3])
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func BenchmarkRows(b *testing.B) {
|
||||
|
@ -934,6 +953,12 @@ func BenchmarkRows(b *testing.B) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
sheet.go
8
sheet.go
|
@ -419,6 +419,9 @@ func (f *File) GetSheetIndex(name string) int {
|
|||
// for index, name := range f.GetSheetMap() {
|
||||
// fmt.Println(index, name)
|
||||
// }
|
||||
// if err = f.Close(); err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
//
|
||||
func (f *File) GetSheetMap() map[int]string {
|
||||
wb := f.workbookReader()
|
||||
|
@ -462,6 +465,9 @@ func (f *File) getSheetMap() map[string]string {
|
|||
if _, ok := f.Pkg.Load(path); ok {
|
||||
maps[v.Name] = path
|
||||
}
|
||||
if _, ok := f.tempFiles.Load(path); ok {
|
||||
maps[v.Name] = path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -858,7 +864,7 @@ func (f *File) searchSheet(name, value string, regSearch bool) (result []string,
|
|||
)
|
||||
|
||||
d = f.sharedStringsReader()
|
||||
decoder := f.xmlNewDecoder(bytes.NewReader(f.readXML(name)))
|
||||
decoder := f.xmlNewDecoder(bytes.NewReader(f.readBytes(name)))
|
||||
for {
|
||||
var token xml.Token
|
||||
token, err = decoder.Token()
|
||||
|
|
|
@ -189,6 +189,7 @@ func TestSearchSheet(t *testing.T) {
|
|||
result, err = f.SearchSheet("Sheet1", "[0-9]", true)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, expected, result)
|
||||
assert.NoError(t, f.Close())
|
||||
|
||||
// Test search worksheet data after set cell value
|
||||
f = NewFile()
|
||||
|
@ -327,6 +328,7 @@ func TestGetSheetName(t *testing.T) {
|
|||
assert.Equal(t, "Sheet2", f.GetSheetName(1))
|
||||
assert.Equal(t, "", f.GetSheetName(-1))
|
||||
assert.Equal(t, "", f.GetSheetName(2))
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestGetSheetMap(t *testing.T) {
|
||||
|
@ -341,6 +343,7 @@ func TestGetSheetMap(t *testing.T) {
|
|||
assert.Equal(t, expectedMap[idx], name)
|
||||
}
|
||||
assert.Equal(t, len(sheetMap), 2)
|
||||
assert.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func TestSetActiveSheet(t *testing.T) {
|
||||
|
|
|
@ -115,6 +115,21 @@ func TestStreamWriter(t *testing.T) {
|
|||
cellValue, err := file.GetCellValue("Sheet1", "A1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Data", cellValue)
|
||||
|
||||
// Test stream reader for a worksheet with huge amounts of data.
|
||||
file, err = OpenFile(filepath.Join("test", "TestStreamWriter.xlsx"))
|
||||
assert.NoError(t, err)
|
||||
rows, err := file.Rows("Sheet1")
|
||||
assert.NoError(t, err)
|
||||
cells := 0
|
||||
for rows.Next() {
|
||||
row, err := rows.Columns()
|
||||
assert.NoError(t, err)
|
||||
cells += len(row)
|
||||
}
|
||||
assert.NoError(t, rows.Close())
|
||||
assert.Equal(t, 2559558, cells)
|
||||
assert.NoError(t, file.Close())
|
||||
}
|
||||
|
||||
func TestStreamSetColWidth(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue