file.go 6.0 KB
Newer Older
1
// Copyright 2016 - 2022 The excelize Authors. All rights reserved. Use of
xurime's avatar
xurime 已提交
2 3 4
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
5 6 7 8 9 10
// Package excelize providing a set of functions that allow you to write to and
// read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
// writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
// Supports complex components by high compatibility, and provided streaming
// API for generating or reading data from a worksheet with huge amounts of
// data. This library needs Go version 1.15 or later.
xurime's avatar
xurime 已提交
11

xurime's avatar
xurime 已提交
12 13 14 15 16
package excelize

import (
	"archive/zip"
	"bytes"
17
	"encoding/xml"
J
Josh Fyne 已提交
18
	"io"
xurime's avatar
xurime 已提交
19
	"os"
xurime's avatar
xurime 已提交
20
	"path/filepath"
21
	"sync"
xurime's avatar
xurime 已提交
22 23
)

xurime's avatar
xurime 已提交
24
// NewFile provides a function to create new file by default template. For
25 26
// example:
//
27
//    f := NewFile()
28
//
29
func NewFile() *File {
30
	f := newFile()
31
	f.Pkg.Store("_rels/.rels", []byte(xml.Header+templateRels))
32 33
	f.Pkg.Store(defaultXMLPathDocPropsApp, []byte(xml.Header+templateDocpropsApp))
	f.Pkg.Store(defaultXMLPathDocPropsCore, []byte(xml.Header+templateDocpropsCore))
34 35 36 37 38 39
	f.Pkg.Store("xl/_rels/workbook.xml.rels", []byte(xml.Header+templateWorkbookRels))
	f.Pkg.Store("xl/theme/theme1.xml", []byte(xml.Header+templateTheme))
	f.Pkg.Store("xl/worksheets/sheet1.xml", []byte(xml.Header+templateSheet))
	f.Pkg.Store(defaultXMLPathStyles, []byte(xml.Header+templateStyles))
	f.Pkg.Store(defaultXMLPathWorkbook, []byte(xml.Header+templateWorkbook))
	f.Pkg.Store(defaultXMLPathContentTypes, []byte(xml.Header+templateContentTypes))
40
	f.SheetCount = 1
41 42
	f.CalcChain = f.calcChainReader()
	f.Comments = make(map[string]*xlsxComments)
43
	f.ContentTypes = f.contentTypesReader()
44
	f.Drawings = sync.Map{}
45
	f.Styles = f.stylesReader()
46 47
	f.DecodeVMLDrawing = make(map[string]*decodeVmlDrawing)
	f.VMLDrawing = make(map[string]*vmlDrawing)
48
	f.WorkBook = f.workbookReader()
49 50
	f.Relationships = sync.Map{}
	f.Relationships.Store("xl/_rels/workbook.xml.rels", f.relsReader("xl/_rels/workbook.xml.rels"))
51
	f.sheetMap["Sheet1"] = "xl/worksheets/sheet1.xml"
52 53
	ws, _ := f.workSheetReader("Sheet1")
	f.Sheet.Store("xl/worksheets/sheet1.xml", ws)
xurime's avatar
xurime 已提交
54
	f.Theme = f.themeReader()
55
	return f
56 57
}

58
// Save provides a function to override the spreadsheet with origin path.
59
func (f *File) Save() error {
J
Josh Fyne 已提交
60
	if f.Path == "" {
61
		return ErrSave
62
	}
63
	return f.SaveAs(f.Path)
xurime's avatar
xurime 已提交
64 65
}

66
// SaveAs provides a function to create or update to a spreadsheet at the
xurime's avatar
xurime 已提交
67
// provided path.
xurime's avatar
xurime 已提交
68
func (f *File) SaveAs(name string, opt ...Options) error {
69 70
	if len(name) > MaxFilePathLength {
		return ErrMaxFilePathLength
71
	}
72
	f.Path = name
73 74 75 76 77 78 79 80
	contentType, ok := map[string]string{
		".xlam": ContentTypeAddinMacro,
		".xlsm": ContentTypeMacro,
		".xlsx": ContentTypeSheetML,
		".xltm": ContentTypeTemplateMacro,
		".xltx": ContentTypeTemplate,
	}[filepath.Ext(f.Path)]
	if !ok {
81
		return ErrWorkbookFileFormat
82 83
	}
	f.setContentTypePartProjectExtensions(contentType)
84
	file, err := os.OpenFile(filepath.Clean(name), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
J
Josh Fyne 已提交
85 86 87 88
	if err != nil {
		return err
	}
	defer file.Close()
89
	f.options = nil
xurime's avatar
xurime 已提交
90 91
	for i := range opt {
		f.options = &opt[i]
xurime's avatar
xurime 已提交
92
	}
J
Josh Fyne 已提交
93 94 95
	return f.Write(file)
}

96 97 98
// Close closes and cleanup the open temporary file for the spreadsheet.
func (f *File) Close() error {
	var err error
99 100 101 102 103
	if f.sharedStringTemp != nil {
		if err := f.sharedStringTemp.Close(); err != nil {
			return err
		}
	}
104 105 106 107 108 109 110 111 112
	f.tempFiles.Range(func(k, v interface{}) bool {
		if err = os.Remove(v.(string)); err != nil {
			return false
		}
		return true
	})
	return err
}

xurime's avatar
xurime 已提交
113
// Write provides a function to write to an io.Writer.
J
Josh Fyne 已提交
114
func (f *File) Write(w io.Writer) error {
115 116 117 118 119 120
	_, err := f.WriteTo(w)
	return err
}

// WriteTo implements io.WriterTo to write the file.
func (f *File) WriteTo(w io.Writer) (int64, error) {
121 122 123 124 125 126 127 128
	if f.options != nil && f.options.Password != "" {
		buf, err := f.WriteToBuffer()
		if err != nil {
			return 0, err
		}
		return buf.WriteTo(w)
	}
	if err := f.writeDirectToWriter(w); err != nil {
129 130
		return 0, err
	}
131
	return 0, nil
132 133
}

xurime's avatar
xurime 已提交
134 135
// WriteToBuffer provides a function to get bytes.Buffer from the saved file,
// and it allocates space in memory. Be careful when the file size is large.
136
func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
xurime's avatar
xurime 已提交
137
	buf := new(bytes.Buffer)
J
Josh Fyne 已提交
138
	zw := zip.NewWriter(buf)
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162

	if err := f.writeToZip(zw); err != nil {
		return buf, zw.Close()
	}

	if f.options != nil && f.options.Password != "" {
		if err := zw.Close(); err != nil {
			return buf, err
		}
		b, err := Encrypt(buf.Bytes(), f.options)
		if err != nil {
			return buf, err
		}
		buf.Reset()
		buf.Write(b)
		return buf, nil
	}
	return buf, zw.Close()
}

// writeDirectToWriter provides a function to write to io.Writer.
func (f *File) writeDirectToWriter(w io.Writer) error {
	zw := zip.NewWriter(w)
	if err := f.writeToZip(zw); err != nil {
xurime's avatar
xurime 已提交
163
		_ = zw.Close()
164 165 166 167 168 169 170
		return err
	}
	return zw.Close()
}

// writeToZip provides a function to write to zip.Writer
func (f *File) writeToZip(zw *zip.Writer) error {
171 172
	f.calcChainWriter()
	f.commentsWriter()
xurime's avatar
xurime 已提交
173
	f.contentTypesWriter()
174
	f.drawingsWriter()
175
	f.vmlDrawingWriter()
176 177
	f.workBookWriter()
	f.workSheetWriter()
xurime's avatar
xurime 已提交
178
	f.relsWriter()
179
	f.sharedStringsLoader()
xurime's avatar
xurime 已提交
180
	f.sharedStringsWriter()
181
	f.styleSheetWriter()
182

183 184 185
	for path, stream := range f.streams {
		fi, err := zw.Create(path)
		if err != nil {
186
			return err
187 188 189 190
		}
		var from io.Reader
		from, err = stream.rawData.Reader()
		if err != nil {
xurime's avatar
xurime 已提交
191
			_ = stream.rawData.Close()
192
			return err
193 194 195
		}
		_, err = io.Copy(fi, from)
		if err != nil {
196
			return err
197
		}
xurime's avatar
xurime 已提交
198
		_ = stream.rawData.Close()
199
	}
200 201
	var err error
	f.Pkg.Range(func(path, content interface{}) bool {
xurime's avatar
xurime 已提交
202
		if err != nil {
203
			return false
xurime's avatar
xurime 已提交
204
		}
205 206 207 208 209
		if _, ok := f.streams[path.(string)]; ok {
			return true
		}
		var fi io.Writer
		fi, err = zw.Create(path.(string))
xurime's avatar
xurime 已提交
210
		if err != nil {
211
			return false
xurime's avatar
xurime 已提交
212
		}
213 214 215
		_, err = fi.Write(content.([]byte))
		return true
	})
216
	f.tempFiles.Range(func(path, content interface{}) bool {
217 218 219
		if _, ok := f.Pkg.Load(path); ok {
			return true
		}
220 221 222 223 224 225 226 227
		var fi io.Writer
		fi, err = zw.Create(path.(string))
		if err != nil {
			return false
		}
		_, err = fi.Write(f.readBytes(path.(string)))
		return true
	})
228
	return err
xurime's avatar
xurime 已提交
229
}