file.go 4.9 KB
Newer Older
1
// Copyright 2016 - 2021 The excelize Authors. All rights reserved. Use of
xurime's avatar
xurime 已提交
2 3 4 5
// this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
//
// Package excelize providing a set of functions that allow you to write to
xurime's avatar
xurime 已提交
6
// and read from XLSX / XLSM / XLTM files. Supports reading and writing
xurime's avatar
xurime 已提交
7
// spreadsheet documents generated by Microsoft Excel™ 2007 and later. Supports
xurime's avatar
xurime 已提交
8 9
// complex components by high compatibility, and provided streaming API for
// generating or reading data from a worksheet with huge amounts of data. This
10
// 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"
J
Josh Fyne 已提交
17 18
	"fmt"
	"io"
xurime's avatar
xurime 已提交
19 20 21
	"os"
)

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

56
// Save provides a function to override the spreadsheet with origin path.
57
func (f *File) Save() error {
J
Josh Fyne 已提交
58
	if f.Path == "" {
xurime's avatar
xurime 已提交
59
		return fmt.Errorf("no path defined for file, consider File.WriteTo or File.Write")
60
	}
61
	return f.SaveAs(f.Path)
xurime's avatar
xurime 已提交
62 63
}

64
// SaveAs provides a function to create or update to an spreadsheet at the
xurime's avatar
xurime 已提交
65
// provided path.
xurime's avatar
xurime 已提交
66
func (f *File) SaveAs(name string, opt ...Options) error {
67
	if len(name) > MaxFileNameLength {
68
		return ErrMaxFileNameLength
69
	}
J
Josh Fyne 已提交
70 71 72 73 74
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer file.Close()
75
	f.options = nil
xurime's avatar
xurime 已提交
76 77 78
	for _, o := range opt {
		f.options = &o
	}
J
Josh Fyne 已提交
79 80 81
	return f.Write(file)
}

xurime's avatar
xurime 已提交
82
// Write provides a function to write to an io.Writer.
J
Josh Fyne 已提交
83
func (f *File) Write(w io.Writer) error {
84 85 86 87 88 89
	_, err := f.WriteTo(w)
	return err
}

// WriteTo implements io.WriterTo to write the file.
func (f *File) WriteTo(w io.Writer) (int64, error) {
90 91 92 93 94 95 96 97
	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 {
98 99
		return 0, err
	}
100
	return 0, nil
101 102
}

103
// WriteToBuffer provides a function to get bytes.Buffer from the saved file. And it allocate space in memory. Be careful when the file size is large.
104
func (f *File) WriteToBuffer() (*bytes.Buffer, error) {
xurime's avatar
xurime 已提交
105
	buf := new(bytes.Buffer)
J
Josh Fyne 已提交
106
	zw := zip.NewWriter(buf)
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

	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 {
		zw.Close()
		return err
	}
	return zw.Close()
}

// writeToZip provides a function to write to zip.Writer
func (f *File) writeToZip(zw *zip.Writer) error {
139 140
	f.calcChainWriter()
	f.commentsWriter()
xurime's avatar
xurime 已提交
141
	f.contentTypesWriter()
142
	f.drawingsWriter()
143
	f.vmlDrawingWriter()
144 145
	f.workBookWriter()
	f.workSheetWriter()
xurime's avatar
xurime 已提交
146
	f.relsWriter()
xurime's avatar
xurime 已提交
147
	f.sharedStringsWriter()
148
	f.styleSheetWriter()
149

150 151 152
	for path, stream := range f.streams {
		fi, err := zw.Create(path)
		if err != nil {
153
			return err
154 155 156 157 158
		}
		var from io.Reader
		from, err = stream.rawData.Reader()
		if err != nil {
			stream.rawData.Close()
159
			return err
160 161 162
		}
		_, err = io.Copy(fi, from)
		if err != nil {
163
			return err
164 165 166 167
		}
		stream.rawData.Close()
	}

168
	for path, content := range f.XLSX {
169 170 171
		if _, ok := f.streams[path]; ok {
			continue
		}
J
Josh Fyne 已提交
172
		fi, err := zw.Create(path)
xurime's avatar
xurime 已提交
173
		if err != nil {
174
			return err
xurime's avatar
xurime 已提交
175
		}
176
		_, err = fi.Write(content)
xurime's avatar
xurime 已提交
177
		if err != nil {
178
			return err
xurime's avatar
xurime 已提交
179 180
		}
	}
xurime's avatar
xurime 已提交
181

182
	return nil
xurime's avatar
xurime 已提交
183
}