stream.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // Copyright 2016 - 2020 The excelize Authors. All rights reserved. Use of
  2. // this source code is governed by a BSD-style license that can be found in
  3. // the LICENSE file.
  4. //
  5. // Package excelize providing a set of functions that allow you to write to
  6. // and read from XLSX files. Support reads and writes XLSX file generated by
  7. // Microsoft Excel™ 2007 and later. Support save file without losing original
  8. // charts of XLSX. This library needs Go version 1.10 or later.
  9. package excelize
  10. import (
  11. "bytes"
  12. "encoding/xml"
  13. "fmt"
  14. "io"
  15. "io/ioutil"
  16. "os"
  17. "reflect"
  18. "strconv"
  19. "strings"
  20. "time"
  21. )
  22. // StreamWriter defined the type of stream writer.
  23. type StreamWriter struct {
  24. File *File
  25. Sheet string
  26. SheetID int
  27. rawData bufferedWriter
  28. tableParts string
  29. }
  30. // NewStreamWriter return stream writer struct by given worksheet name for
  31. // generate new worksheet with large amounts of data. Note that after set
  32. // rows, you must call the 'Flush' method to end the streaming writing
  33. // process and ensure that the order of line numbers is ascending. For
  34. // example, set data for worksheet of size 102400 rows x 50 columns with
  35. // numbers and style:
  36. //
  37. // file := excelize.NewFile()
  38. // streamWriter, err := file.NewStreamWriter("Sheet1")
  39. // if err != nil {
  40. // panic(err)
  41. // }
  42. // styleID, err := file.NewStyle(`{"font":{"color":"#777777"}}`)
  43. // if err != nil {
  44. // panic(err)
  45. // }
  46. // if err := streamWriter.SetRow("A1", []interface{}{excelize.Cell{StyleID: styleID, Value: "Data"}}); err != nil {
  47. // panic(err)
  48. // }
  49. // for rowID := 2; rowID <= 102400; rowID++ {
  50. // row := make([]interface{}, 50)
  51. // for colID := 0; colID < 50; colID++ {
  52. // row[colID] = rand.Intn(640000)
  53. // }
  54. // cell, _ := excelize.CoordinatesToCellName(1, rowID)
  55. // if err := streamWriter.SetRow(cell, row); err != nil {
  56. // panic(err)
  57. // }
  58. // }
  59. // if err := streamWriter.Flush(); err != nil {
  60. // panic(err)
  61. // }
  62. // if err := file.SaveAs("Book1.xlsx"); err != nil {
  63. // panic(err)
  64. // }
  65. //
  66. func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
  67. sheetID := f.GetSheetIndex(sheet)
  68. if sheetID == 0 {
  69. return nil, fmt.Errorf("sheet %s is not exist", sheet)
  70. }
  71. sw := &StreamWriter{
  72. File: f,
  73. Sheet: sheet,
  74. SheetID: sheetID,
  75. }
  76. ws, err := f.workSheetReader(sheet)
  77. if err != nil {
  78. return nil, err
  79. }
  80. sw.rawData.WriteString(XMLHeader + `<worksheet` + templateNamespaceIDMap)
  81. bulkAppendOtherFields(&sw.rawData, ws, "XMLName", "SheetData", "TableParts")
  82. sw.rawData.WriteString(`<sheetData>`)
  83. return sw, nil
  84. }
  85. // AddTable creates an Excel table for the StreamWriter using the given
  86. // coordinate area and format set. For example, create a table of A1:D5:
  87. //
  88. // err := sw.AddTable("A1", "D5", ``)
  89. //
  90. // Create a table of F2:H6 with format set:
  91. //
  92. // err := sw.AddTable("F2", "H6", `{"table_name":"table","table_style":"TableStyleMedium2","show_first_column":true,"show_last_column":true,"show_row_stripes":false,"show_column_stripes":true}`)
  93. //
  94. // Note that the table must be at least two lines including the header. The
  95. // header cells must contain strings and must be unique.
  96. //
  97. // Currently only one table is allowed for a StreamWriter. AddTable must be
  98. // called after the rows are written but before Flush.
  99. //
  100. // See File.AddTable for details on the table format.
  101. func (sw *StreamWriter) AddTable(hcell, vcell, format string) error {
  102. formatSet, err := parseFormatTableSet(format)
  103. if err != nil {
  104. return err
  105. }
  106. coordinates, err := areaRangeToCoordinates(hcell, vcell)
  107. if err != nil {
  108. return err
  109. }
  110. _ = sortCoordinates(coordinates)
  111. // Correct the minimum number of rows, the table at least two lines.
  112. if coordinates[1] == coordinates[3] {
  113. coordinates[3]++
  114. }
  115. // Correct table reference coordinate area, such correct C1:B3 to B1:C3.
  116. ref, err := sw.File.coordinatesToAreaRef(coordinates)
  117. if err != nil {
  118. return err
  119. }
  120. // create table columns using the first row
  121. tableHeaders, err := sw.getRowValues(coordinates[1], coordinates[0], coordinates[2])
  122. if err != nil {
  123. return err
  124. }
  125. tableColumn := make([]*xlsxTableColumn, len(tableHeaders))
  126. for i, name := range tableHeaders {
  127. tableColumn[i] = &xlsxTableColumn{
  128. ID: i + 1,
  129. Name: name,
  130. }
  131. }
  132. tableID := sw.File.countTables() + 1
  133. name := formatSet.TableName
  134. if name == "" {
  135. name = "Table" + strconv.Itoa(tableID)
  136. }
  137. table := xlsxTable{
  138. XMLNS: NameSpaceSpreadSheet,
  139. ID: tableID,
  140. Name: name,
  141. DisplayName: name,
  142. Ref: ref,
  143. AutoFilter: &xlsxAutoFilter{
  144. Ref: ref,
  145. },
  146. TableColumns: &xlsxTableColumns{
  147. Count: len(tableColumn),
  148. TableColumn: tableColumn,
  149. },
  150. TableStyleInfo: &xlsxTableStyleInfo{
  151. Name: formatSet.TableStyle,
  152. ShowFirstColumn: formatSet.ShowFirstColumn,
  153. ShowLastColumn: formatSet.ShowLastColumn,
  154. ShowRowStripes: formatSet.ShowRowStripes,
  155. ShowColumnStripes: formatSet.ShowColumnStripes,
  156. },
  157. }
  158. sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
  159. tableXML := strings.Replace(sheetRelationshipsTableXML, "..", "xl", -1)
  160. // Add first table for given sheet.
  161. sheetPath, _ := sw.File.sheetMap[trimSheetName(sw.Sheet)]
  162. sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
  163. rID := sw.File.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
  164. sw.tableParts = fmt.Sprintf(`<tableParts count="1"><tablePart r:id="rId%d"></tablePart></tableParts>`, rID)
  165. sw.File.addContentTypePart(tableID, "table")
  166. b, _ := xml.Marshal(table)
  167. sw.File.saveFileList(tableXML, b)
  168. return nil
  169. }
  170. // Extract values from a row in the StreamWriter.
  171. func (sw *StreamWriter) getRowValues(hrow, hcol, vcol int) (res []string, err error) {
  172. res = make([]string, vcol-hcol+1)
  173. r, err := sw.rawData.Reader()
  174. if err != nil {
  175. return nil, err
  176. }
  177. dec := sw.File.xmlNewDecoder(r)
  178. for {
  179. token, err := dec.Token()
  180. if err == io.EOF {
  181. return res, nil
  182. }
  183. if err != nil {
  184. return nil, err
  185. }
  186. startElement, ok := getRowElement(token, hrow)
  187. if !ok {
  188. continue
  189. }
  190. // decode cells
  191. var row xlsxRow
  192. if err := dec.DecodeElement(&row, &startElement); err != nil {
  193. return nil, err
  194. }
  195. for _, c := range row.C {
  196. col, _, err := CellNameToCoordinates(c.R)
  197. if err != nil {
  198. return nil, err
  199. }
  200. if col < hcol || col > vcol {
  201. continue
  202. }
  203. res[col-hcol] = c.V
  204. }
  205. return res, nil
  206. }
  207. }
  208. // Check if the token is an XLSX row with the matching row number.
  209. func getRowElement(token xml.Token, hrow int) (startElement xml.StartElement, ok bool) {
  210. startElement, ok = token.(xml.StartElement)
  211. if !ok {
  212. return
  213. }
  214. ok = startElement.Name.Local == "row"
  215. if !ok {
  216. return
  217. }
  218. ok = false
  219. for _, attr := range startElement.Attr {
  220. if attr.Name.Local != "r" {
  221. continue
  222. }
  223. row, _ := strconv.Atoi(attr.Value)
  224. if row == hrow {
  225. ok = true
  226. return
  227. }
  228. }
  229. return
  230. }
  231. // Cell can be used directly in StreamWriter.SetRow to specify a style and
  232. // a value.
  233. type Cell struct {
  234. StyleID int
  235. Value interface{}
  236. }
  237. // SetRow writes an array to stream rows by giving a worksheet name, starting
  238. // coordinate and a pointer to an array of values. Note that you must call the
  239. // 'Flush' method to end the streaming writing process.
  240. //
  241. // As a special case, if Cell is used as a value, then the Cell.StyleID will be
  242. // applied to that cell.
  243. func (sw *StreamWriter) SetRow(axis string, values []interface{}) error {
  244. col, row, err := CellNameToCoordinates(axis)
  245. if err != nil {
  246. return err
  247. }
  248. fmt.Fprintf(&sw.rawData, `<row r="%d">`, row)
  249. for i, val := range values {
  250. axis, err := CoordinatesToCellName(col+i, row)
  251. if err != nil {
  252. return err
  253. }
  254. c := xlsxC{R: axis}
  255. if v, ok := val.(Cell); ok {
  256. c.S = v.StyleID
  257. val = v.Value
  258. } else if v, ok := val.(*Cell); ok && v != nil {
  259. c.S = v.StyleID
  260. val = v.Value
  261. }
  262. if err = setCellValFunc(&c, val); err != nil {
  263. sw.rawData.WriteString(`</row>`)
  264. return err
  265. }
  266. writeCell(&sw.rawData, c)
  267. }
  268. sw.rawData.WriteString(`</row>`)
  269. return sw.rawData.Sync()
  270. }
  271. // setCellValFunc provides a function to set value of a cell.
  272. func setCellValFunc(c *xlsxC, val interface{}) (err error) {
  273. switch val := val.(type) {
  274. case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
  275. err = setCellIntFunc(c, val)
  276. case float32:
  277. c.T, c.V = setCellFloat(float64(val), -1, 32)
  278. case float64:
  279. c.T, c.V = setCellFloat(val, -1, 64)
  280. case string:
  281. c.T, c.V, c.XMLSpace = setCellStr(val)
  282. case []byte:
  283. c.T, c.V, c.XMLSpace = setCellStr(string(val))
  284. case time.Duration:
  285. c.T, c.V = setCellDuration(val)
  286. case time.Time:
  287. c.T, c.V, _, err = setCellTime(val)
  288. case bool:
  289. c.T, c.V = setCellBool(val)
  290. case nil:
  291. c.T, c.V, c.XMLSpace = setCellStr("")
  292. default:
  293. c.T, c.V, c.XMLSpace = setCellStr(fmt.Sprint(val))
  294. }
  295. return err
  296. }
  297. // setCellIntFunc is a wrapper of SetCellInt.
  298. func setCellIntFunc(c *xlsxC, val interface{}) (err error) {
  299. switch val := val.(type) {
  300. case int:
  301. c.T, c.V = setCellInt(val)
  302. case int8:
  303. c.T, c.V = setCellInt(int(val))
  304. case int16:
  305. c.T, c.V = setCellInt(int(val))
  306. case int32:
  307. c.T, c.V = setCellInt(int(val))
  308. case int64:
  309. c.T, c.V = setCellInt(int(val))
  310. case uint:
  311. c.T, c.V = setCellInt(int(val))
  312. case uint8:
  313. c.T, c.V = setCellInt(int(val))
  314. case uint16:
  315. c.T, c.V = setCellInt(int(val))
  316. case uint32:
  317. c.T, c.V = setCellInt(int(val))
  318. case uint64:
  319. c.T, c.V = setCellInt(int(val))
  320. default:
  321. }
  322. return
  323. }
  324. func writeCell(buf *bufferedWriter, c xlsxC) {
  325. buf.WriteString(`<c`)
  326. if c.XMLSpace.Value != "" {
  327. fmt.Fprintf(buf, ` xml:%s="%s"`, c.XMLSpace.Name.Local, c.XMLSpace.Value)
  328. }
  329. fmt.Fprintf(buf, ` r="%s"`, c.R)
  330. if c.S != 0 {
  331. fmt.Fprintf(buf, ` s="%d"`, c.S)
  332. }
  333. if c.T != "" {
  334. fmt.Fprintf(buf, ` t="%s"`, c.T)
  335. }
  336. buf.WriteString(`>`)
  337. if c.V != "" {
  338. buf.WriteString(`<v>`)
  339. xml.EscapeText(buf, []byte(c.V))
  340. buf.WriteString(`</v>`)
  341. }
  342. buf.WriteString(`</c>`)
  343. }
  344. // Flush ending the streaming writing process.
  345. func (sw *StreamWriter) Flush() error {
  346. sw.rawData.WriteString(`</sheetData>`)
  347. sw.rawData.WriteString(sw.tableParts)
  348. sw.rawData.WriteString(`</worksheet>`)
  349. if err := sw.rawData.Flush(); err != nil {
  350. return err
  351. }
  352. sheetXML := fmt.Sprintf("xl/worksheets/sheet%d.xml", sw.SheetID)
  353. delete(sw.File.Sheet, sheetXML)
  354. delete(sw.File.checked, sheetXML)
  355. defer sw.rawData.Close()
  356. b, err := sw.rawData.Bytes()
  357. if err != nil {
  358. return err
  359. }
  360. sw.File.XLSX[sheetXML] = b
  361. return nil
  362. }
  363. // bulkAppendOtherFields bulk-appends fields in a worksheet, skipping the
  364. // specified field names.
  365. func bulkAppendOtherFields(w io.Writer, ws *xlsxWorksheet, skip ...string) {
  366. skipMap := make(map[string]struct{})
  367. for _, name := range skip {
  368. skipMap[name] = struct{}{}
  369. }
  370. s := reflect.ValueOf(ws).Elem()
  371. typeOfT := s.Type()
  372. enc := xml.NewEncoder(w)
  373. for i := 0; i < s.NumField(); i++ {
  374. f := s.Field(i)
  375. if _, ok := skipMap[typeOfT.Field(i).Name]; ok {
  376. continue
  377. }
  378. enc.Encode(f.Interface())
  379. }
  380. }
  381. // bufferedWriter uses a temp file to store an extended buffer. Writes are
  382. // always made to an in-memory buffer, which will always succeed. The buffer
  383. // is written to the temp file with Sync, which may return an error.
  384. // Therefore, Sync should be periodically called and the error checked.
  385. type bufferedWriter struct {
  386. tmp *os.File
  387. buf bytes.Buffer
  388. }
  389. // Write to the in-memory buffer. The err is always nil.
  390. func (bw *bufferedWriter) Write(p []byte) (n int, err error) {
  391. return bw.buf.Write(p)
  392. }
  393. // WriteString wites to the in-memory buffer. The err is always nil.
  394. func (bw *bufferedWriter) WriteString(p string) (n int, err error) {
  395. return bw.buf.WriteString(p)
  396. }
  397. // Reader provides read-access to the underlying buffer/file.
  398. func (bw *bufferedWriter) Reader() (io.Reader, error) {
  399. if bw.tmp == nil {
  400. return bytes.NewReader(bw.buf.Bytes()), nil
  401. }
  402. if err := bw.Flush(); err != nil {
  403. return nil, err
  404. }
  405. fi, err := bw.tmp.Stat()
  406. if err != nil {
  407. return nil, err
  408. }
  409. // os.File.ReadAt does not affect the cursor position and is safe to use here
  410. return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil
  411. }
  412. // Bytes returns the entire content of the bufferedWriter. If a temp file is
  413. // used, Bytes will efficiently allocate a buffer to prevent re-allocations.
  414. func (bw *bufferedWriter) Bytes() ([]byte, error) {
  415. if bw.tmp == nil {
  416. return bw.buf.Bytes(), nil
  417. }
  418. if err := bw.Flush(); err != nil {
  419. return nil, err
  420. }
  421. var buf bytes.Buffer
  422. if fi, err := bw.tmp.Stat(); err == nil {
  423. if size := fi.Size() + bytes.MinRead; size > bytes.MinRead {
  424. if int64(int(size)) == size {
  425. buf.Grow(int(size))
  426. } else {
  427. return nil, bytes.ErrTooLarge
  428. }
  429. }
  430. }
  431. if _, err := bw.tmp.Seek(0, 0); err != nil {
  432. return nil, err
  433. }
  434. _, err := buf.ReadFrom(bw.tmp)
  435. return buf.Bytes(), err
  436. }
  437. // Sync will write the in-memory buffer to a temp file, if the in-memory
  438. // buffer has grown large enough. Any error will be returned.
  439. func (bw *bufferedWriter) Sync() (err error) {
  440. // Try to use local storage
  441. const chunk = 1 << 24
  442. if bw.buf.Len() < chunk {
  443. return nil
  444. }
  445. if bw.tmp == nil {
  446. bw.tmp, err = ioutil.TempFile(os.TempDir(), "excelize-")
  447. if err != nil {
  448. // can not use local storage
  449. return nil
  450. }
  451. }
  452. return bw.Flush()
  453. }
  454. // Flush the entire in-memory buffer to the temp file, if a temp file is being
  455. // used.
  456. func (bw *bufferedWriter) Flush() error {
  457. if bw.tmp == nil {
  458. return nil
  459. }
  460. _, err := bw.buf.WriteTo(bw.tmp)
  461. if err != nil {
  462. return err
  463. }
  464. bw.buf.Reset()
  465. return nil
  466. }
  467. // Close the underlying temp file and reset the in-memory buffer.
  468. func (bw *bufferedWriter) Close() error {
  469. bw.buf.Reset()
  470. if bw.tmp == nil {
  471. return nil
  472. }
  473. defer os.Remove(bw.tmp.Name())
  474. return bw.tmp.Close()
  475. }