Optimize memory usage when stream flush and save (#659)

* use io.Copy from stream temp file to zip Writer

* fix nil

* log

* build

* delete log

* fix compatibility for office

* Update go module

Co-authored-by: lijingfeng <lijingfeng@laiye.com>
Co-authored-by: xuri <xuri.me@gmail.com>
This commit is contained in:
Lijingfeng 2020-10-05 22:17:11 +08:00 committed by GitHub
parent f2b8798a34
commit 93160287bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 37 deletions

View File

@ -36,6 +36,7 @@ type File struct {
xmlAttr map[string][]xml.Attr xmlAttr map[string][]xml.Attr
checked map[string]bool checked map[string]bool
sheetMap map[string]string sheetMap map[string]string
streams map[string]*StreamWriter
CalcChain *xlsxCalcChain CalcChain *xlsxCalcChain
Comments map[string]*xlsxComments Comments map[string]*xlsxComments
ContentTypes *xlsxTypes ContentTypes *xlsxTypes

20
file.go
View File

@ -110,6 +110,26 @@ func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
f.sharedStringsWriter() f.sharedStringsWriter()
f.styleSheetWriter() f.styleSheetWriter()
for path, stream := range f.streams {
fi, err := zw.Create(path)
if err != nil {
zw.Close()
return buf, err
}
var from io.Reader
from, err = stream.rawData.Reader()
if err != nil {
stream.rawData.Close()
return buf, err
}
_, err = io.Copy(fi, from)
if err != nil {
zw.Close()
return buf, err
}
stream.rawData.Close()
}
for path, content := range f.XLSX { for path, content := range f.XLSX {
fi, err := zw.Create(path) fi, err := zw.Create(path)
if err != nil { if err != nil {

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/360EntSecGroup-Skylar/excelize/v2 module github.com/360EntSecGroup-Skylar/excelize/v2
go 1.15 go 1.11
require ( require (
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826

View File

@ -85,6 +85,13 @@ func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
if f.streams == nil {
f.streams = make(map[string]*StreamWriter)
}
f.streams[sheetXML] = sw
sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap) sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
bulkAppendFields(&sw.rawData, sw.worksheet, 2, 6) bulkAppendFields(&sw.rawData, sw.worksheet, 2, 6)
sw.rawData.WriteString(`<sheetData>`) sw.rawData.WriteString(`<sheetData>`)
@ -387,13 +394,8 @@ func (sw *StreamWriter) Flush() error {
sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID) sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
delete(sw.File.Sheet, sheetXML) delete(sw.File.Sheet, sheetXML)
delete(sw.File.checked, sheetXML) delete(sw.File.checked, sheetXML)
delete(sw.File.XLSX, sheetXML)
defer sw.rawData.Close()
b, err := sw.rawData.Bytes()
if err != nil {
return err
}
sw.File.XLSX[sheetXML] = b
return nil return nil
} }
@ -444,36 +446,6 @@ func (bw *bufferedWriter) Reader() (io.Reader, error) {
return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil
} }
// Bytes returns the entire content of the bufferedWriter. If a temp file is
// used, Bytes will efficiently allocate a buffer to prevent re-allocations.
func (bw *bufferedWriter) Bytes() ([]byte, error) {
if bw.tmp == nil {
return bw.buf.Bytes(), nil
}
if err := bw.Flush(); err != nil {
return nil, err
}
var buf bytes.Buffer
if fi, err := bw.tmp.Stat(); err == nil {
if size := fi.Size() + bytes.MinRead; size > bytes.MinRead {
if int64(int(size)) == size {
buf.Grow(int(size))
} else {
return nil, bytes.ErrTooLarge
}
}
}
if _, err := bw.tmp.Seek(0, 0); err != nil {
return nil, err
}
_, err := buf.ReadFrom(bw.tmp)
return buf.Bytes(), err
}
// Sync will write the in-memory buffer to a temp file, if the in-memory // Sync will write the in-memory buffer to a temp file, if the in-memory
// buffer has grown large enough. Any error will be returned. // buffer has grown large enough. Any error will be returned.
func (bw *bufferedWriter) Sync() (err error) { func (bw *bufferedWriter) Sync() (err error) {