@@ -30,7 +30,7 @@ type StreamWriter struct {
3030 SheetID int
3131 sheetWritten bool
3232 worksheet * xlsxWorksheet
33- rawData bufferedWriter
33+ rawData TmpFile
3434 rows int
3535 mergeCellsCount int
3636 mergeCells strings.Builder
@@ -119,11 +119,17 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
119119 if sheetID == - 1 {
120120 return nil , ErrSheetNotExist {sheet }
121121 }
122+
123+ rawData := TmpFile (newBufferedWriter (f .options .TmpDir , nil ))
124+ if f .options .StreamingTmpFile != nil {
125+ rawData = * f .options .StreamingTmpFile
126+ }
127+
122128 sw := & StreamWriter {
123129 file : f ,
124130 Sheet : sheet ,
125131 SheetID : sheetID ,
126- rawData : bufferedWriter { tmpDir : f . options . TmpDir } ,
132+ rawData : rawData ,
127133 }
128134 var err error
129135 sw .worksheet , err = f .workSheetReader (sheet )
@@ -138,7 +144,7 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
138144 f .streams [sheetXMLPath ] = sw
139145
140146 _ , _ = sw .rawData .WriteString (xml .Header + `<worksheet` + templateNamespaceIDMap )
141- bulkAppendFields (& sw .rawData , sw .worksheet , 3 , 4 )
147+ bulkAppendFields (sw .rawData , sw .worksheet , 3 , 4 )
142148 return sw , err
143149}
144150
@@ -429,7 +435,7 @@ func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpt
429435 _ , _ = sw .rawData .WriteString (`</row>` )
430436 return err
431437 }
432- writeCell (& sw .rawData , c )
438+ writeCell (sw .rawData , c )
433439 }
434440 _ , _ = sw .rawData .WriteString (`</row>` )
435441 return sw .rawData .Sync ()
@@ -602,7 +608,7 @@ func setCellIntFunc(c *xlsxC, val interface{}) {
602608}
603609
604610// writeCell constructs a cell XML and writes it to the buffer.
605- func writeCell (buf * bufferedWriter , c xlsxC ) {
611+ func writeCell (buf TmpFile , c xlsxC ) {
606612 _ , _ = buf .WriteString (`<c` )
607613 if c .XMLSpace .Value != "" {
608614 _ , _ = buf .WriteString (` xml:` )
@@ -663,7 +669,7 @@ func writeCell(buf *bufferedWriter, c xlsxC) {
663669// sheetData XML start element to the buffer.
664670func (sw * StreamWriter ) writeSheetData () {
665671 if ! sw .sheetWritten {
666- bulkAppendFields (& sw .rawData , sw .worksheet , 5 , 6 )
672+ bulkAppendFields (sw .rawData , sw .worksheet , 5 , 6 )
667673 if sw .worksheet .Cols != nil {
668674 _ , _ = sw .rawData .WriteString ("<cols>" )
669675 for _ , col := range sw .worksheet .Cols .Col {
@@ -695,7 +701,7 @@ func (sw *StreamWriter) writeSheetData() {
695701func (sw * StreamWriter ) Flush () error {
696702 sw .writeSheetData ()
697703 _ , _ = sw .rawData .WriteString (`</sheetData>` )
698- bulkAppendFields (& sw .rawData , sw .worksheet , 9 , 16 )
704+ bulkAppendFields (sw .rawData , sw .worksheet , 9 , 16 )
699705 mergeCells := strings.Builder {}
700706 if sw .mergeCellsCount > 0 {
701707 _ , _ = mergeCells .WriteString (`<mergeCells count="` )
@@ -705,9 +711,9 @@ func (sw *StreamWriter) Flush() error {
705711 _ , _ = mergeCells .WriteString (`</mergeCells>` )
706712 }
707713 _ , _ = sw .rawData .WriteString (mergeCells .String ())
708- bulkAppendFields (& sw .rawData , sw .worksheet , 18 , 39 )
714+ bulkAppendFields (sw .rawData , sw .worksheet , 18 , 39 )
709715 _ , _ = sw .rawData .WriteString (sw .tableParts )
710- bulkAppendFields (& sw .rawData , sw .worksheet , 41 , 41 )
716+ bulkAppendFields (sw .rawData , sw .worksheet , 41 , 41 )
711717 _ , _ = sw .rawData .WriteString (`</worksheet>` )
712718 if err := sw .rawData .Flush (); err != nil {
713719 return err
@@ -733,11 +739,38 @@ func bulkAppendFields(w io.Writer, ws *xlsxWorksheet, from, to int) {
733739 }
734740}
735741
742+ // TmpFile is an interface for a streaming writer temporary file abstraction, implement it to support
743+ // custom temporary file storage.
744+ type TmpFile interface {
745+ Close () error
746+ Reader () (io.Reader , error )
747+ Sync () error
748+ Write (p []byte ) (n int , err error )
749+ WriteString (s string ) (n int , err error )
750+ Flush () error
751+ }
752+
753+ // newBufferedWriter create a new bufferedWriter, which will write to a temp
754+ // file if the buffer size exceeds the chunkSize. when chunkSize is nil, the
755+ // default chunkSize which is StreamChunkSize will be used.
756+ func newBufferedWriter (tmpDir string , chunkSize * int ) * bufferedWriter {
757+ tarChunkSize := StreamChunkSize
758+ if chunkSize != nil {
759+ tarChunkSize = * chunkSize
760+ }
761+ return & bufferedWriter {
762+ chunkSize : tarChunkSize ,
763+ tmpDir : tmpDir ,
764+ }
765+ }
766+
736767// bufferedWriter uses a temp file to store an extended buffer. Writes are
737768// always made to an in-memory buffer, which will always succeed. The buffer
738769// is written to the temp file with Sync, which may return an error.
739770// Therefore, Sync should be periodically called and the error checked.
740771type bufferedWriter struct {
772+ chunkSize int
773+
741774 tmpDir string
742775 tmp * os.File
743776 buf bytes.Buffer
0 commit comments