diff --git a/excelize.go b/excelize.go index ea8926a3b2..6449f03892 100644 --- a/excelize.go +++ b/excelize.go @@ -101,17 +101,24 @@ type charsetTranscoderFn func(charset string, input io.Reader) (rdr io.Reader, e // // CultureInfo specifies the country code for applying built-in language number // format code these effect by the system's local language settings. +// +// TmpDir specifies the temporary directory for creating temporary files, if the +// value is empty, the system default temporary directory will be used. +// +// StreamingTmpFile specifies the temporary file for streaming writing streaming writer temporary file, +// if the value is nil, the system default temporary file will be used to write streaming data. type Options struct { MaxCalcIterations uint Password string RawCellValue bool UnzipSizeLimit int64 UnzipXMLSizeLimit int64 - TmpDir string ShortDatePattern string LongDatePattern string LongTimePattern string CultureInfo CultureName + TmpDir string + StreamingTmpFile *TmpFile } // OpenFile take the name of a spreadsheet file and returns a populated diff --git a/file_test.go b/file_test.go index 58c9e4a265..b6a0fe862b 100644 --- a/file_test.go +++ b/file_test.go @@ -61,9 +61,13 @@ func TestWriteTo(t *testing.T) { f, buf := File{Pkg: sync.Map{}}, bytes.Buffer{} f.Pkg.Store("s", nil) f.streams = make(map[string]*StreamWriter) - file, _ := os.Open("123") - f.streams["s"] = &StreamWriter{rawData: bufferedWriter{tmp: file}} - _, err := f.WriteTo(bufio.NewWriter(&buf)) + file, err := os.Open("123") + assert.Error(t, err) + + rawData := newBufferedWriter(f.options.TmpDir, nil) + rawData.tmp = file + f.streams["s"] = &StreamWriter{rawData: rawData} + _, err = f.WriteTo(bufio.NewWriter(&buf)) assert.Nil(t, err) } // Test write with temporary file diff --git a/lib.go b/lib.go index 7c5cdfd78a..bd038e410a 100644 --- a/lib.go +++ b/lib.go @@ -103,7 +103,15 @@ func (f *File) readXML(name string) []byte { return content.([]byte) } if content, ok := f.streams[name]; ok { - return content.rawData.buf.Bytes() + rawDataReader, err := content.rawData.Reader() + if err != nil { + return []byte{} + } + rawDataContent, err := io.ReadAll(rawDataReader) + if err != nil { + return []byte{} + } + return rawDataContent } return []byte{} } diff --git a/stream.go b/stream.go index e4844b6958..c064ee6fae 100644 --- a/stream.go +++ b/stream.go @@ -30,7 +30,7 @@ type StreamWriter struct { SheetID int sheetWritten bool worksheet *xlsxWorksheet - rawData bufferedWriter + rawData TmpFile rows int mergeCellsCount int mergeCells strings.Builder @@ -119,11 +119,17 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { if sheetID == -1 { return nil, ErrSheetNotExist{sheet} } + + rawData := TmpFile(newBufferedWriter(f.options.TmpDir, nil)) + if f.options.StreamingTmpFile != nil { + rawData = *f.options.StreamingTmpFile + } + sw := &StreamWriter{ file: f, Sheet: sheet, SheetID: sheetID, - rawData: bufferedWriter{tmpDir: f.options.TmpDir}, + rawData: rawData, } var err error sw.worksheet, err = f.workSheetReader(sheet) @@ -138,7 +144,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) { f.streams[sheetXMLPath] = sw _, _ = sw.rawData.WriteString(xml.Header + ``) return err } - writeCell(&sw.rawData, c) + writeCell(sw.rawData, c) } _, _ = sw.rawData.WriteString(``) return sw.rawData.Sync() @@ -602,7 +608,7 @@ func setCellIntFunc(c *xlsxC, val interface{}) { } // writeCell constructs a cell XML and writes it to the buffer. -func writeCell(buf *bufferedWriter, c xlsxC) { +func writeCell(buf TmpFile, c xlsxC) { _, _ = buf.WriteString(`") for _, col := range sw.worksheet.Cols.Col { @@ -695,7 +701,7 @@ func (sw *StreamWriter) writeSheetData() { func (sw *StreamWriter) Flush() error { sw.writeSheetData() _, _ = sw.rawData.WriteString(``) - bulkAppendFields(&sw.rawData, sw.worksheet, 9, 16) + bulkAppendFields(sw.rawData, sw.worksheet, 9, 16) mergeCells := strings.Builder{} if sw.mergeCellsCount > 0 { _, _ = mergeCells.WriteString(`") + bw.buf.WriteString("") _, err = sw.getRowValues(1, 1, 1) assert.NoError(t, err) - sw.rawData.buf.Reset() + bw.buf.Reset() // Test getRowValues with illegal cell reference - sw.rawData.buf.WriteString("") + bw.buf.WriteString("") _, err = sw.getRowValues(1, 1, 1) assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), err) - sw.rawData.buf.Reset() + bw.buf.Reset() // Test getRowValues with invalid c element characters - sw.rawData.buf.WriteString("") + bw.buf.WriteString("") _, err = sw.getRowValues(1, 1, 1) assert.EqualError(t, err, "XML syntax error on line 1: element closed by ") - sw.rawData.buf.Reset() + bw.buf.Reset() } func TestStreamWriterGetRowElement(t *testing.T) {