file.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package xlsx
  2. import (
  3. "archive/zip"
  4. "encoding/xml"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. )
  10. // File is a high level structure providing a slice of Sheet structs
  11. // to the user.
  12. type File struct {
  13. worksheets map[string]*zip.File
  14. referenceTable *RefTable
  15. Date1904 bool
  16. styles *xlsxStyleSheet
  17. Sheets []*Sheet
  18. Sheet map[string]*Sheet
  19. }
  20. // Create a new File
  21. func NewFile() (file *File) {
  22. file = &File{}
  23. file.Sheet = make(map[string]*Sheet)
  24. file.Sheets = make([]*Sheet, 0)
  25. return
  26. }
  27. // OpenFile() take the name of an XLSX file and returns a populated
  28. // xlsx.File struct for it.
  29. func OpenFile(filename string) (file *File, err error) {
  30. var f *zip.ReadCloser
  31. f, err = zip.OpenReader(filename)
  32. if err != nil {
  33. return nil, err
  34. }
  35. file, err = ReadZip(f)
  36. return
  37. }
  38. // A convenient wrapper around File.ToSlice, FileToSlice will
  39. // return the raw data contained in an Excel XLSX file as three
  40. // dimensional slice. The first index represents the sheet number,
  41. // the second the row number, and the third the cell number.
  42. //
  43. // For example:
  44. //
  45. // var mySlice [][][]string
  46. // var value string
  47. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  48. // value = mySlice[0][0][0]
  49. //
  50. // Here, value would be set to the raw value of the cell A1 in the
  51. // first sheet in the XLSX file.
  52. func FileToSlice(path string) ([][][]string, error) {
  53. f, err := OpenFile(path)
  54. if err != nil {
  55. return nil, err
  56. }
  57. return f.ToSlice()
  58. }
  59. // Save the File to an xlsx file at the provided path.
  60. func (f *File) Save(path string) (err error) {
  61. var target *os.File
  62. target, err = os.Create(path)
  63. if err != nil {
  64. return
  65. }
  66. err = f.Write(target)
  67. if err != nil {
  68. return
  69. }
  70. return target.Close()
  71. }
  72. // Write the File to io.Writer as xlsx
  73. func (f *File) Write(writer io.Writer) (err error) {
  74. var parts map[string]string
  75. var zipWriter *zip.Writer
  76. parts, err = f.MarshallParts()
  77. if err != nil {
  78. return
  79. }
  80. zipWriter = zip.NewWriter(writer)
  81. for partName, part := range parts {
  82. var writer io.Writer
  83. writer, err = zipWriter.Create(partName)
  84. if err != nil {
  85. return
  86. }
  87. _, err = writer.Write([]byte(part))
  88. if err != nil {
  89. return
  90. }
  91. }
  92. err = zipWriter.Close()
  93. return
  94. }
  95. // Add a new Sheet, with the provided name, to a File
  96. func (f *File) AddSheet(sheetName string) (sheet *Sheet) {
  97. sheet = &Sheet{Name: sheetName, File: *f}
  98. f.Sheet[sheetName] = sheet
  99. f.Sheets = append(f.Sheets, sheet)
  100. return sheet
  101. }
  102. func (f *File) makeWorkbook() xlsxWorkbook {
  103. var workbook xlsxWorkbook
  104. workbook = xlsxWorkbook{}
  105. workbook.FileVersion = xlsxFileVersion{}
  106. workbook.FileVersion.AppName = "Go XLSX"
  107. workbook.WorkbookPr = xlsxWorkbookPr{
  108. BackupFile: false,
  109. ShowObjects: "all"}
  110. workbook.BookViews = xlsxBookViews{}
  111. workbook.BookViews.WorkBookView = make([]xlsxWorkBookView, 1)
  112. workbook.BookViews.WorkBookView[0] = xlsxWorkBookView{
  113. ActiveTab: 0,
  114. FirstSheet: 0,
  115. ShowHorizontalScroll: true,
  116. ShowSheetTabs: true,
  117. ShowVerticalScroll: true,
  118. TabRatio: 204,
  119. WindowHeight: 8192,
  120. WindowWidth: 16384,
  121. XWindow: "0",
  122. YWindow: "0"}
  123. workbook.Sheets = xlsxSheets{}
  124. workbook.Sheets.Sheet = make([]xlsxSheet, len(f.Sheets))
  125. workbook.CalcPr.IterateCount = 100
  126. workbook.CalcPr.RefMode = "A1"
  127. workbook.CalcPr.Iterate = false
  128. workbook.CalcPr.IterateDelta = 0.001
  129. return workbook
  130. }
  131. // Construct a map of file name to XML content representing the file
  132. // in terms of the structure of an XLSX file.
  133. func (f *File) MarshallParts() (map[string]string, error) {
  134. var parts map[string]string
  135. var refTable *RefTable = NewSharedStringRefTable()
  136. refTable.isWrite = true
  137. var workbookRels WorkBookRels = make(WorkBookRels)
  138. var err error
  139. var workbook xlsxWorkbook
  140. var types xlsxTypes = MakeDefaultContentTypes()
  141. marshal := func(thing interface{}) (string, error) {
  142. body, err := xml.Marshal(thing)
  143. if err != nil {
  144. return "", err
  145. }
  146. return xml.Header + string(body), nil
  147. }
  148. parts = make(map[string]string)
  149. workbook = f.makeWorkbook()
  150. sheetIndex := 1
  151. if f.styles == nil {
  152. f.styles = &xlsxStyleSheet{}
  153. }
  154. f.styles.reset()
  155. for _, sheet := range f.Sheets {
  156. xSheet := sheet.makeXLSXSheet(refTable, f.styles)
  157. rId := fmt.Sprintf("rId%d", sheetIndex)
  158. sheetId := strconv.Itoa(sheetIndex)
  159. sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
  160. partName := "xl/" + sheetPath
  161. types.Overrides = append(
  162. types.Overrides,
  163. xlsxOverride{
  164. PartName: "/" + partName,
  165. ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"})
  166. workbookRels[rId] = sheetPath
  167. workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
  168. Name: sheet.Name,
  169. SheetId: sheetId,
  170. Id: rId,
  171. State: "visible"}
  172. parts[partName], err = marshal(xSheet)
  173. if err != nil {
  174. return parts, err
  175. }
  176. sheetIndex++
  177. }
  178. parts["xl/workbook.xml"], err = marshal(workbook)
  179. if err != nil {
  180. return parts, err
  181. }
  182. parts["_rels/.rels"] = TEMPLATE__RELS_DOT_RELS
  183. parts["docProps/app.xml"] = TEMPLATE_DOCPROPS_APP
  184. // TODO - do this properly, modification and revision information
  185. parts["docProps/core.xml"] = TEMPLATE_DOCPROPS_CORE
  186. parts["xl/theme/theme1.xml"] = TEMPLATE_XL_THEME_THEME
  187. xSST := refTable.makeXLSXSST()
  188. parts["xl/sharedStrings.xml"], err = marshal(xSST)
  189. if err != nil {
  190. return parts, err
  191. }
  192. xWRel := workbookRels.MakeXLSXWorkbookRels()
  193. parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
  194. if err != nil {
  195. return parts, err
  196. }
  197. parts["[Content_Types].xml"], err = marshal(types)
  198. if err != nil {
  199. return parts, err
  200. }
  201. parts["xl/styles.xml"], err = f.styles.Marshal()
  202. if err != nil {
  203. return parts, err
  204. }
  205. return parts, nil
  206. }
  207. // Return the raw data contained in the File as three
  208. // dimensional slice. The first index represents the sheet number,
  209. // the second the row number, and the third the cell number.
  210. //
  211. // For example:
  212. //
  213. // var mySlice [][][]string
  214. // var value string
  215. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  216. // value = mySlice[0][0][0]
  217. //
  218. // Here, value would be set to the raw value of the cell A1 in the
  219. // first sheet in the XLSX file.
  220. func (file *File) ToSlice() (output [][][]string, err error) {
  221. output = [][][]string{}
  222. for _, sheet := range file.Sheets {
  223. s := [][]string{}
  224. for _, row := range sheet.Rows {
  225. if row == nil {
  226. continue
  227. }
  228. r := []string{}
  229. for _, cell := range row.Cells {
  230. r = append(r, cell.String())
  231. }
  232. s = append(s, r)
  233. }
  234. output = append(output, s)
  235. }
  236. return output, nil
  237. }