file.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. numFmtRefTable map[int]xlsxNumFmt
  15. referenceTable *RefTable
  16. Date1904 bool
  17. styles *xlsxStyleSheet
  18. Sheets []*Sheet
  19. Sheet map[string]*Sheet
  20. }
  21. // Create a new File
  22. func NewFile() (file *File) {
  23. file = &File{}
  24. file.Sheet = make(map[string]*Sheet)
  25. file.Sheets = make([]*Sheet, 0)
  26. return
  27. }
  28. // OpenFile() take the name of an XLSX file and returns a populated
  29. // xlsx.File struct for it.
  30. func OpenFile(filename string) (*File, error) {
  31. var f *zip.ReadCloser
  32. f, err := zip.OpenReader(filename)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return ReadZip(f)
  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 parts map[string]string
  62. var target *os.File
  63. var zipWriter *zip.Writer
  64. parts, err = f.MarshallParts()
  65. if err != nil {
  66. return
  67. }
  68. target, err = os.Create(path)
  69. if err != nil {
  70. return
  71. }
  72. zipWriter = zip.NewWriter(target)
  73. for partName, part := range parts {
  74. var writer io.Writer
  75. writer, err = zipWriter.Create(partName)
  76. if err != nil {
  77. return
  78. }
  79. _, err = writer.Write([]byte(part))
  80. if err != nil {
  81. return
  82. }
  83. }
  84. err = zipWriter.Close()
  85. if err != nil {
  86. return
  87. }
  88. return target.Close()
  89. }
  90. // Add a new Sheet, with the provided name, to a File
  91. func (f *File) AddSheet(sheetName string) (sheet *Sheet) {
  92. sheet = &Sheet{Name: sheetName}
  93. f.Sheet[sheetName] = sheet
  94. f.Sheets = append(f.Sheets, sheet)
  95. return sheet
  96. }
  97. func (f *File) makeWorkbook() xlsxWorkbook {
  98. var workbook xlsxWorkbook
  99. workbook = xlsxWorkbook{}
  100. workbook.FileVersion = xlsxFileVersion{}
  101. workbook.FileVersion.AppName = "Go XLSX"
  102. workbook.WorkbookPr = xlsxWorkbookPr{BackupFile: false}
  103. workbook.BookViews = xlsxBookViews{}
  104. workbook.BookViews.WorkBookView = make([]xlsxWorkBookView, 1)
  105. workbook.BookViews.WorkBookView[0] = xlsxWorkBookView{}
  106. workbook.Sheets = xlsxSheets{}
  107. workbook.Sheets.Sheet = make([]xlsxSheet, len(f.Sheets))
  108. return workbook
  109. }
  110. // Construct a map of file name to XML content representing the file
  111. // in terms of the structure of an XLSX file.
  112. func (f *File) MarshallParts() (map[string]string, error) {
  113. var parts map[string]string
  114. var refTable *RefTable = NewSharedStringRefTable()
  115. refTable.isWrite = true
  116. var workbookRels WorkBookRels = make(WorkBookRels)
  117. var err error
  118. var workbook xlsxWorkbook
  119. var types xlsxTypes = MakeDefaultContentTypes()
  120. marshal := func(thing interface{}) (string, error) {
  121. body, err := xml.MarshalIndent(thing, " ", " ")
  122. if err != nil {
  123. return "", err
  124. }
  125. return xml.Header + string(body), nil
  126. }
  127. parts = make(map[string]string)
  128. workbook = f.makeWorkbook()
  129. sheetIndex := 1
  130. styles := &xlsxStyleSheet{}
  131. for _, sheet := range f.Sheets {
  132. xSheet := sheet.makeXLSXSheet(refTable, styles)
  133. rId := fmt.Sprintf("rId%d", sheetIndex)
  134. sheetId := strconv.Itoa(sheetIndex)
  135. sheetPath := fmt.Sprintf("worksheets/sheet%d.xml", sheetIndex)
  136. partName := "xl/" + sheetPath
  137. types.Overrides = append(
  138. types.Overrides,
  139. xlsxOverride{
  140. PartName: "/" + partName,
  141. ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"})
  142. workbookRels[rId] = sheetPath
  143. workbook.Sheets.Sheet[sheetIndex-1] = xlsxSheet{
  144. Name: sheet.Name,
  145. SheetId: sheetId,
  146. Id: rId}
  147. parts[partName], err = marshal(xSheet)
  148. if err != nil {
  149. return parts, err
  150. }
  151. sheetIndex++
  152. }
  153. parts["xl/workbook.xml"], err = marshal(workbook)
  154. if err != nil {
  155. return parts, err
  156. }
  157. parts["_rels/.rels"] = TEMPLATE__RELS_DOT_RELS
  158. parts["docProps/app.xml"] = TEMPLATE_DOCPROPS_APP
  159. // TODO - do this properly, modification and revision information
  160. parts["docProps/core.xml"] = TEMPLATE_DOCPROPS_CORE
  161. parts["xl/theme/theme1.xml"] = TEMPLATE_XL_THEME_THEME
  162. xSST := refTable.makeXLSXSST()
  163. parts["xl/sharedStrings.xml"], err = marshal(xSST)
  164. if err != nil {
  165. return parts, err
  166. }
  167. xWRel := workbookRels.MakeXLSXWorkbookRels()
  168. parts["xl/_rels/workbook.xml.rels"], err = marshal(xWRel)
  169. if err != nil {
  170. return parts, err
  171. }
  172. parts["[Content_Types].xml"], err = marshal(types)
  173. if err != nil {
  174. return parts, err
  175. }
  176. parts["xl/styles.xml"], err = marshal(styles)
  177. if err != nil {
  178. return parts, err
  179. }
  180. return parts, nil
  181. }
  182. // Return the raw data contained in the File as three
  183. // dimensional slice. The first index represents the sheet number,
  184. // the second the row number, and the third the cell number.
  185. //
  186. // For example:
  187. //
  188. // var mySlice [][][]string
  189. // var value string
  190. // mySlice = xlsx.FileToSlice("myXLSX.xlsx")
  191. // value = mySlice[0][0][0]
  192. //
  193. // Here, value would be set to the raw value of the cell A1 in the
  194. // first sheet in the XLSX file.
  195. func (file *File) ToSlice() (output [][][]string, err error) {
  196. output = [][][]string{}
  197. for _, sheet := range file.Sheets {
  198. s := [][]string{}
  199. for _, row := range sheet.Rows {
  200. if row == nil {
  201. continue
  202. }
  203. r := []string{}
  204. for _, cell := range row.Cells {
  205. r = append(r, cell.String())
  206. }
  207. s = append(s, r)
  208. }
  209. output = append(output, s)
  210. }
  211. return output, nil
  212. }