stream.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. // Copyright 2016 - 2023 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 and
  6. // read from XLAM / XLSM / XLSX / XLTM / XLTX files. Supports reading and
  7. // writing spreadsheet documents generated by Microsoft Excel™ 2007 and later.
  8. // Supports complex components by high compatibility, and provided streaming
  9. // API for generating or reading data from a worksheet with huge amounts of
  10. // data. This library needs Go version 1.16 or later.
  11. package excelize
  12. import (
  13. "bytes"
  14. "encoding/xml"
  15. "fmt"
  16. "io"
  17. "os"
  18. "reflect"
  19. "strconv"
  20. "strings"
  21. "time"
  22. )
  23. // StreamWriter defined the type of stream writer.
  24. type StreamWriter struct {
  25. file *File
  26. Sheet string
  27. SheetID int
  28. sheetWritten bool
  29. cols strings.Builder
  30. worksheet *xlsxWorksheet
  31. rawData bufferedWriter
  32. rows int
  33. mergeCellsCount int
  34. mergeCells strings.Builder
  35. tableParts string
  36. }
  37. // NewStreamWriter return stream writer struct by given worksheet name for
  38. // generate new worksheet with large amounts of data. Note that after set
  39. // rows, you must call the 'Flush' method to end the streaming writing process
  40. // and ensure that the order of row numbers is ascending, the normal mode
  41. // functions and stream mode functions can't be work mixed to writing data on
  42. // the worksheets, you can't get cell value when in-memory chunks data over
  43. // 16MB. For example, set data for worksheet of size 102400 rows x 50 columns
  44. // with numbers and style:
  45. //
  46. // f := excelize.NewFile()
  47. // defer func() {
  48. // if err := f.Close(); err != nil {
  49. // fmt.Println(err)
  50. // }
  51. // }()
  52. // sw, err := f.NewStreamWriter("Sheet1")
  53. // if err != nil {
  54. // fmt.Println(err)
  55. // return
  56. // }
  57. // styleID, err := f.NewStyle(&excelize.Style{Font: &excelize.Font{Color: "777777"}})
  58. // if err != nil {
  59. // fmt.Println(err)
  60. // return
  61. // }
  62. // if err := sw.SetRow("A1",
  63. // []interface{}{
  64. // excelize.Cell{StyleID: styleID, Value: "Data"},
  65. // []excelize.RichTextRun{
  66. // {Text: "Rich ", Font: &excelize.Font{Color: "2354e8"}},
  67. // {Text: "Text", Font: &excelize.Font{Color: "e83723"}},
  68. // },
  69. // },
  70. // excelize.RowOpts{Height: 45, Hidden: false}); err != nil {
  71. // fmt.Println(err)
  72. // return
  73. // }
  74. // for rowID := 2; rowID <= 102400; rowID++ {
  75. // row := make([]interface{}, 50)
  76. // for colID := 0; colID < 50; colID++ {
  77. // row[colID] = rand.Intn(640000)
  78. // }
  79. // cell, err := excelize.CoordinatesToCellName(1, rowID)
  80. // if err != nil {
  81. // fmt.Println(err)
  82. // break
  83. // }
  84. // if err := sw.SetRow(cell, row); err != nil {
  85. // fmt.Println(err)
  86. // break
  87. // }
  88. // }
  89. // if err := sw.Flush(); err != nil {
  90. // fmt.Println(err)
  91. // return
  92. // }
  93. // if err := f.SaveAs("Book1.xlsx"); err != nil {
  94. // fmt.Println(err)
  95. // }
  96. //
  97. // Set cell value and cell formula for a worksheet with stream writer:
  98. //
  99. // err := sw.SetRow("A1", []interface{}{
  100. // excelize.Cell{Value: 1},
  101. // excelize.Cell{Value: 2},
  102. // excelize.Cell{Formula: "SUM(A1,B1)"}});
  103. //
  104. // Set cell value and rows style for a worksheet with stream writer:
  105. //
  106. // err := sw.SetRow("A1", []interface{}{
  107. // excelize.Cell{Value: 1}},
  108. // excelize.RowOpts{StyleID: styleID, Height: 20, Hidden: false});
  109. func (f *File) NewStreamWriter(sheet string) (*StreamWriter, error) {
  110. if err := checkSheetName(sheet); err != nil {
  111. return nil, err
  112. }
  113. sheetID := f.getSheetID(sheet)
  114. if sheetID == -1 {
  115. return nil, newNoExistSheetError(sheet)
  116. }
  117. sw := &StreamWriter{
  118. file: f,
  119. Sheet: sheet,
  120. SheetID: sheetID,
  121. }
  122. var err error
  123. sw.worksheet, err = f.workSheetReader(sheet)
  124. if err != nil {
  125. return nil, err
  126. }
  127. sheetXMLPath, _ := f.getSheetXMLPath(sheet)
  128. if f.streams == nil {
  129. f.streams = make(map[string]*StreamWriter)
  130. }
  131. f.streams[sheetXMLPath] = sw
  132. _, _ = sw.rawData.WriteString(xml.Header + `<worksheet` + templateNamespaceIDMap)
  133. bulkAppendFields(&sw.rawData, sw.worksheet, 2, 3)
  134. return sw, err
  135. }
  136. // AddTable creates an Excel table for the StreamWriter using the given
  137. // cell range and format set. For example, create a table of A1:D5:
  138. //
  139. // err := sw.AddTable(&excelize.Table{Range: "A1:D5"})
  140. //
  141. // Create a table of F2:H6 with format set:
  142. //
  143. // disable := false
  144. // err := sw.AddTable(&excelize.Table{
  145. // Range: "F2:H6",
  146. // Name: "table",
  147. // StyleName: "TableStyleMedium2",
  148. // ShowFirstColumn: true,
  149. // ShowLastColumn: true,
  150. // ShowRowStripes: &disable,
  151. // ShowColumnStripes: true,
  152. // })
  153. //
  154. // Note that the table must be at least two lines including the header. The
  155. // header cells must contain strings and must be unique.
  156. //
  157. // Currently, only one table is allowed for a StreamWriter. AddTable must be
  158. // called after the rows are written but before Flush.
  159. //
  160. // See File.AddTable for details on the table format.
  161. func (sw *StreamWriter) AddTable(table *Table) error {
  162. options, err := parseTableOptions(table)
  163. if err != nil {
  164. return err
  165. }
  166. coordinates, err := rangeRefToCoordinates(options.Range)
  167. if err != nil {
  168. return err
  169. }
  170. _ = sortCoordinates(coordinates)
  171. // Correct the minimum number of rows, the table at least two lines.
  172. if coordinates[1] == coordinates[3] {
  173. coordinates[3]++
  174. }
  175. // Correct table reference range, such correct C1:B3 to B1:C3.
  176. ref, err := sw.file.coordinatesToRangeRef(coordinates)
  177. if err != nil {
  178. return err
  179. }
  180. // create table columns using the first row
  181. tableHeaders, err := sw.getRowValues(coordinates[1], coordinates[0], coordinates[2])
  182. if err != nil {
  183. return err
  184. }
  185. tableColumn := make([]*xlsxTableColumn, len(tableHeaders))
  186. for i, name := range tableHeaders {
  187. tableColumn[i] = &xlsxTableColumn{
  188. ID: i + 1,
  189. Name: name,
  190. }
  191. }
  192. tableID := sw.file.countTables() + 1
  193. name := options.Name
  194. if name == "" {
  195. name = "Table" + strconv.Itoa(tableID)
  196. }
  197. tbl := xlsxTable{
  198. XMLNS: NameSpaceSpreadSheet.Value,
  199. ID: tableID,
  200. Name: name,
  201. DisplayName: name,
  202. Ref: ref,
  203. AutoFilter: &xlsxAutoFilter{
  204. Ref: ref,
  205. },
  206. TableColumns: &xlsxTableColumns{
  207. Count: len(tableColumn),
  208. TableColumn: tableColumn,
  209. },
  210. TableStyleInfo: &xlsxTableStyleInfo{
  211. Name: options.StyleName,
  212. ShowFirstColumn: options.ShowFirstColumn,
  213. ShowLastColumn: options.ShowLastColumn,
  214. ShowRowStripes: *options.ShowRowStripes,
  215. ShowColumnStripes: options.ShowColumnStripes,
  216. },
  217. }
  218. sheetRelationshipsTableXML := "../tables/table" + strconv.Itoa(tableID) + ".xml"
  219. tableXML := strings.ReplaceAll(sheetRelationshipsTableXML, "..", "xl")
  220. // Add first table for given sheet
  221. sheetPath := sw.file.sheetMap[sw.Sheet]
  222. sheetRels := "xl/worksheets/_rels/" + strings.TrimPrefix(sheetPath, "xl/worksheets/") + ".rels"
  223. rID := sw.file.addRels(sheetRels, SourceRelationshipTable, sheetRelationshipsTableXML, "")
  224. sw.tableParts = fmt.Sprintf(`<tableParts count="1"><tablePart r:id="rId%d"></tablePart></tableParts>`, rID)
  225. if err = sw.file.addContentTypePart(tableID, "table"); err != nil {
  226. return err
  227. }
  228. b, _ := xml.Marshal(tbl)
  229. sw.file.saveFileList(tableXML, b)
  230. return err
  231. }
  232. // Extract values from a row in the StreamWriter.
  233. func (sw *StreamWriter) getRowValues(hRow, hCol, vCol int) (res []string, err error) {
  234. res = make([]string, vCol-hCol+1)
  235. r, err := sw.rawData.Reader()
  236. if err != nil {
  237. return nil, err
  238. }
  239. dec := sw.file.xmlNewDecoder(r)
  240. for {
  241. token, err := dec.Token()
  242. if err == io.EOF {
  243. return res, nil
  244. }
  245. if err != nil {
  246. return nil, err
  247. }
  248. startElement, ok := getRowElement(token, hRow)
  249. if !ok {
  250. continue
  251. }
  252. // decode cells
  253. var row xlsxRow
  254. if err := dec.DecodeElement(&row, &startElement); err != nil {
  255. return nil, err
  256. }
  257. for _, c := range row.C {
  258. col, _, err := CellNameToCoordinates(c.R)
  259. if err != nil {
  260. return nil, err
  261. }
  262. if col < hCol || col > vCol {
  263. continue
  264. }
  265. res[col-hCol], _ = c.getValueFrom(sw.file, nil, false)
  266. }
  267. return res, nil
  268. }
  269. }
  270. // Check if the token is an XLSX row with the matching row number.
  271. func getRowElement(token xml.Token, hRow int) (startElement xml.StartElement, ok bool) {
  272. startElement, ok = token.(xml.StartElement)
  273. if !ok {
  274. return
  275. }
  276. ok = startElement.Name.Local == "row"
  277. if !ok {
  278. return
  279. }
  280. ok = false
  281. for _, attr := range startElement.Attr {
  282. if attr.Name.Local != "r" {
  283. continue
  284. }
  285. row, _ := strconv.Atoi(attr.Value)
  286. if row == hRow {
  287. ok = true
  288. return
  289. }
  290. }
  291. return
  292. }
  293. // Cell can be used directly in StreamWriter.SetRow to specify a style and
  294. // a value.
  295. type Cell struct {
  296. StyleID int
  297. Formula string
  298. Value interface{}
  299. }
  300. // RowOpts define the options for the set row, it can be used directly in
  301. // StreamWriter.SetRow to specify the style and properties of the row.
  302. type RowOpts struct {
  303. Height float64
  304. Hidden bool
  305. StyleID int
  306. OutlineLevel int
  307. }
  308. // marshalAttrs prepare attributes of the row.
  309. func (r *RowOpts) marshalAttrs() (strings.Builder, error) {
  310. var (
  311. err error
  312. attrs strings.Builder
  313. )
  314. if r == nil {
  315. return attrs, err
  316. }
  317. if r.Height > MaxRowHeight {
  318. err = ErrMaxRowHeight
  319. return attrs, err
  320. }
  321. if r.OutlineLevel > 7 {
  322. err = ErrOutlineLevel
  323. return attrs, err
  324. }
  325. if r.StyleID > 0 {
  326. attrs.WriteString(` s="`)
  327. attrs.WriteString(strconv.Itoa(r.StyleID))
  328. attrs.WriteString(`" customFormat="1"`)
  329. }
  330. if r.Height > 0 {
  331. attrs.WriteString(` ht="`)
  332. attrs.WriteString(strconv.FormatFloat(r.Height, 'f', -1, 64))
  333. attrs.WriteString(`" customHeight="1"`)
  334. }
  335. if r.OutlineLevel > 0 {
  336. attrs.WriteString(` outlineLevel="`)
  337. attrs.WriteString(strconv.Itoa(r.OutlineLevel))
  338. attrs.WriteString(`"`)
  339. }
  340. if r.Hidden {
  341. attrs.WriteString(` hidden="1"`)
  342. }
  343. return attrs, err
  344. }
  345. // parseRowOpts provides a function to parse the optional settings for
  346. // *StreamWriter.SetRow.
  347. func parseRowOpts(opts ...RowOpts) *RowOpts {
  348. options := &RowOpts{}
  349. for _, opt := range opts {
  350. options = &opt
  351. }
  352. return options
  353. }
  354. // SetRow writes an array to stream rows by giving starting cell reference and a
  355. // pointer to an array of values. Note that you must call the 'Flush' function
  356. // to end the streaming writing process.
  357. //
  358. // As a special case, if Cell is used as a value, then the Cell.StyleID will be
  359. // applied to that cell.
  360. func (sw *StreamWriter) SetRow(cell string, values []interface{}, opts ...RowOpts) error {
  361. col, row, err := CellNameToCoordinates(cell)
  362. if err != nil {
  363. return err
  364. }
  365. if row <= sw.rows {
  366. return newStreamSetRowError(row)
  367. }
  368. sw.rows = row
  369. sw.writeSheetData()
  370. options := parseRowOpts(opts...)
  371. attrs, err := options.marshalAttrs()
  372. if err != nil {
  373. return err
  374. }
  375. _, _ = sw.rawData.WriteString(`<row r="`)
  376. _, _ = sw.rawData.WriteString(strconv.Itoa(row))
  377. _, _ = sw.rawData.WriteString(`"`)
  378. _, _ = sw.rawData.WriteString(attrs.String())
  379. _, _ = sw.rawData.WriteString(`>`)
  380. for i, val := range values {
  381. if val == nil {
  382. continue
  383. }
  384. ref, err := CoordinatesToCellName(col+i, row)
  385. if err != nil {
  386. return err
  387. }
  388. c := xlsxC{R: ref, S: options.StyleID}
  389. if v, ok := val.(Cell); ok {
  390. c.S = v.StyleID
  391. val = v.Value
  392. setCellFormula(&c, v.Formula)
  393. } else if v, ok := val.(*Cell); ok && v != nil {
  394. c.S = v.StyleID
  395. val = v.Value
  396. setCellFormula(&c, v.Formula)
  397. }
  398. if err = sw.setCellValFunc(&c, val); err != nil {
  399. _, _ = sw.rawData.WriteString(`</row>`)
  400. return err
  401. }
  402. writeCell(&sw.rawData, c)
  403. }
  404. _, _ = sw.rawData.WriteString(`</row>`)
  405. return sw.rawData.Sync()
  406. }
  407. // SetColWidth provides a function to set the width of a single column or
  408. // multiple columns for the StreamWriter. Note that you must call
  409. // the 'SetColWidth' function before the 'SetRow' function. For example set
  410. // the width column B:C as 20:
  411. //
  412. // err := sw.SetColWidth(2, 3, 20)
  413. func (sw *StreamWriter) SetColWidth(min, max int, width float64) error {
  414. if sw.sheetWritten {
  415. return ErrStreamSetColWidth
  416. }
  417. if min < MinColumns || min > MaxColumns || max < MinColumns || max > MaxColumns {
  418. return ErrColumnNumber
  419. }
  420. if width > MaxColumnWidth {
  421. return ErrColumnWidth
  422. }
  423. if min > max {
  424. min, max = max, min
  425. }
  426. sw.cols.WriteString(`<col min="`)
  427. sw.cols.WriteString(strconv.Itoa(min))
  428. sw.cols.WriteString(`" max="`)
  429. sw.cols.WriteString(strconv.Itoa(max))
  430. sw.cols.WriteString(`" width="`)
  431. sw.cols.WriteString(strconv.FormatFloat(width, 'f', -1, 64))
  432. sw.cols.WriteString(`" customWidth="1"/>`)
  433. return nil
  434. }
  435. // InsertPageBreak creates a page break to determine where the printed page ends
  436. // and where begins the next one by a given cell reference, the content before
  437. // the page break will be printed on one page and after the page break on
  438. // another.
  439. func (sw *StreamWriter) InsertPageBreak(cell string) error {
  440. return sw.worksheet.insertPageBreak(cell)
  441. }
  442. // SetPanes provides a function to create and remove freeze panes and split
  443. // panes by giving panes options for the StreamWriter. Note that you must call
  444. // the 'SetPanes' function before the 'SetRow' function.
  445. func (sw *StreamWriter) SetPanes(panes *Panes) error {
  446. if sw.sheetWritten {
  447. return ErrStreamSetPanes
  448. }
  449. return sw.worksheet.setPanes(panes)
  450. }
  451. // MergeCell provides a function to merge cells by a given range reference for
  452. // the StreamWriter. Don't create a merged cell that overlaps with another
  453. // existing merged cell.
  454. func (sw *StreamWriter) MergeCell(hCell, vCell string) error {
  455. _, err := cellRefsToCoordinates(hCell, vCell)
  456. if err != nil {
  457. return err
  458. }
  459. sw.mergeCellsCount++
  460. _, _ = sw.mergeCells.WriteString(`<mergeCell ref="`)
  461. _, _ = sw.mergeCells.WriteString(hCell)
  462. _, _ = sw.mergeCells.WriteString(`:`)
  463. _, _ = sw.mergeCells.WriteString(vCell)
  464. _, _ = sw.mergeCells.WriteString(`"/>`)
  465. return nil
  466. }
  467. // setCellFormula provides a function to set formula of a cell.
  468. func setCellFormula(c *xlsxC, formula string) {
  469. if formula != "" {
  470. c.T, c.F = "str", &xlsxF{Content: formula}
  471. }
  472. }
  473. // setCellTime provides a function to set number of a cell with a time.
  474. func (sw *StreamWriter) setCellTime(c *xlsxC, val time.Time) error {
  475. var date1904, isNum bool
  476. wb, err := sw.file.workbookReader()
  477. if err != nil {
  478. return err
  479. }
  480. if wb != nil && wb.WorkbookPr != nil {
  481. date1904 = wb.WorkbookPr.Date1904
  482. }
  483. if isNum, err = c.setCellTime(val, date1904); err == nil && isNum && c.S == 0 {
  484. style, _ := sw.file.NewStyle(&Style{NumFmt: 22})
  485. c.S = style
  486. }
  487. return nil
  488. }
  489. // setCellValFunc provides a function to set value of a cell.
  490. func (sw *StreamWriter) setCellValFunc(c *xlsxC, val interface{}) error {
  491. var err error
  492. switch val := val.(type) {
  493. case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
  494. err = setCellIntFunc(c, val)
  495. case float32:
  496. c.T, c.V = setCellFloat(float64(val), -1, 32)
  497. case float64:
  498. c.T, c.V = setCellFloat(val, -1, 64)
  499. case string:
  500. c.setCellValue(val)
  501. case []byte:
  502. c.setCellValue(string(val))
  503. case time.Duration:
  504. c.T, c.V = setCellDuration(val)
  505. case time.Time:
  506. err = sw.setCellTime(c, val)
  507. case bool:
  508. c.T, c.V = setCellBool(val)
  509. case nil:
  510. return err
  511. case []RichTextRun:
  512. c.T, c.IS = "inlineStr", &xlsxSI{}
  513. c.IS.R, err = setRichText(val)
  514. default:
  515. c.setCellValue(fmt.Sprint(val))
  516. }
  517. return err
  518. }
  519. // setCellIntFunc is a wrapper of SetCellInt.
  520. func setCellIntFunc(c *xlsxC, val interface{}) (err error) {
  521. switch val := val.(type) {
  522. case int:
  523. c.T, c.V = setCellInt(val)
  524. case int8:
  525. c.T, c.V = setCellInt(int(val))
  526. case int16:
  527. c.T, c.V = setCellInt(int(val))
  528. case int32:
  529. c.T, c.V = setCellInt(int(val))
  530. case int64:
  531. c.T, c.V = setCellInt(int(val))
  532. case uint:
  533. c.T, c.V = setCellInt(int(val))
  534. case uint8:
  535. c.T, c.V = setCellInt(int(val))
  536. case uint16:
  537. c.T, c.V = setCellInt(int(val))
  538. case uint32:
  539. c.T, c.V = setCellInt(int(val))
  540. case uint64:
  541. c.T, c.V = setCellInt(int(val))
  542. default:
  543. }
  544. return
  545. }
  546. // writeCell constructs a cell XML and writes it to the buffer.
  547. func writeCell(buf *bufferedWriter, c xlsxC) {
  548. _, _ = buf.WriteString(`<c`)
  549. if c.XMLSpace.Value != "" {
  550. _, _ = buf.WriteString(` xml:`)
  551. _, _ = buf.WriteString(c.XMLSpace.Name.Local)
  552. _, _ = buf.WriteString(`="`)
  553. _, _ = buf.WriteString(c.XMLSpace.Value)
  554. _, _ = buf.WriteString(`"`)
  555. }
  556. _, _ = buf.WriteString(` r="`)
  557. _, _ = buf.WriteString(c.R)
  558. _, _ = buf.WriteString(`"`)
  559. if c.S != 0 {
  560. _, _ = buf.WriteString(` s="`)
  561. _, _ = buf.WriteString(strconv.Itoa(c.S))
  562. _, _ = buf.WriteString(`"`)
  563. }
  564. if c.T != "" {
  565. _, _ = buf.WriteString(` t="`)
  566. _, _ = buf.WriteString(c.T)
  567. _, _ = buf.WriteString(`"`)
  568. }
  569. _, _ = buf.WriteString(`>`)
  570. if c.F != nil {
  571. _, _ = buf.WriteString(`<f>`)
  572. _ = xml.EscapeText(buf, []byte(c.F.Content))
  573. _, _ = buf.WriteString(`</f>`)
  574. }
  575. if c.V != "" {
  576. _, _ = buf.WriteString(`<v>`)
  577. _ = xml.EscapeText(buf, []byte(c.V))
  578. _, _ = buf.WriteString(`</v>`)
  579. }
  580. if c.IS != nil {
  581. if len(c.IS.R) > 0 {
  582. is, _ := xml.Marshal(c.IS.R)
  583. _, _ = buf.WriteString(`<is>`)
  584. _, _ = buf.Write(is)
  585. _, _ = buf.WriteString(`</is>`)
  586. }
  587. if c.IS.T != nil {
  588. _, _ = buf.WriteString(`<is><t`)
  589. if c.IS.T.Space.Value != "" {
  590. _, _ = buf.WriteString(` xml:`)
  591. _, _ = buf.WriteString(c.IS.T.Space.Name.Local)
  592. _, _ = buf.WriteString(`="`)
  593. _, _ = buf.WriteString(c.IS.T.Space.Value)
  594. _, _ = buf.WriteString(`"`)
  595. }
  596. _, _ = buf.WriteString(`>`)
  597. _, _ = buf.Write([]byte(c.IS.T.Val))
  598. _, _ = buf.WriteString(`</t></is>`)
  599. }
  600. }
  601. _, _ = buf.WriteString(`</c>`)
  602. }
  603. // writeSheetData prepares the element preceding sheetData and writes the
  604. // sheetData XML start element to the buffer.
  605. func (sw *StreamWriter) writeSheetData() {
  606. if !sw.sheetWritten {
  607. bulkAppendFields(&sw.rawData, sw.worksheet, 4, 5)
  608. if sw.cols.Len() > 0 {
  609. _, _ = sw.rawData.WriteString("<cols>")
  610. _, _ = sw.rawData.WriteString(sw.cols.String())
  611. _, _ = sw.rawData.WriteString("</cols>")
  612. }
  613. _, _ = sw.rawData.WriteString(`<sheetData>`)
  614. sw.sheetWritten = true
  615. }
  616. }
  617. // Flush ending the streaming writing process.
  618. func (sw *StreamWriter) Flush() error {
  619. sw.writeSheetData()
  620. _, _ = sw.rawData.WriteString(`</sheetData>`)
  621. bulkAppendFields(&sw.rawData, sw.worksheet, 8, 15)
  622. mergeCells := strings.Builder{}
  623. if sw.mergeCellsCount > 0 {
  624. _, _ = mergeCells.WriteString(`<mergeCells count="`)
  625. _, _ = mergeCells.WriteString(strconv.Itoa(sw.mergeCellsCount))
  626. _, _ = mergeCells.WriteString(`">`)
  627. _, _ = mergeCells.WriteString(sw.mergeCells.String())
  628. _, _ = mergeCells.WriteString(`</mergeCells>`)
  629. }
  630. _, _ = sw.rawData.WriteString(mergeCells.String())
  631. bulkAppendFields(&sw.rawData, sw.worksheet, 17, 38)
  632. _, _ = sw.rawData.WriteString(sw.tableParts)
  633. bulkAppendFields(&sw.rawData, sw.worksheet, 40, 40)
  634. _, _ = sw.rawData.WriteString(`</worksheet>`)
  635. if err := sw.rawData.Flush(); err != nil {
  636. return err
  637. }
  638. sheetPath := sw.file.sheetMap[sw.Sheet]
  639. sw.file.Sheet.Delete(sheetPath)
  640. delete(sw.file.checked, sheetPath)
  641. sw.file.Pkg.Delete(sheetPath)
  642. return nil
  643. }
  644. // bulkAppendFields bulk-appends fields in a worksheet by specified field
  645. // names order range.
  646. func bulkAppendFields(w io.Writer, ws *xlsxWorksheet, from, to int) {
  647. s := reflect.ValueOf(ws).Elem()
  648. enc := xml.NewEncoder(w)
  649. for i := 0; i < s.NumField(); i++ {
  650. if from <= i && i <= to {
  651. _ = enc.Encode(s.Field(i).Interface())
  652. }
  653. }
  654. }
  655. // bufferedWriter uses a temp file to store an extended buffer. Writes are
  656. // always made to an in-memory buffer, which will always succeed. The buffer
  657. // is written to the temp file with Sync, which may return an error.
  658. // Therefore, Sync should be periodically called and the error checked.
  659. type bufferedWriter struct {
  660. tmp *os.File
  661. buf bytes.Buffer
  662. }
  663. // Write to the in-memory buffer. The error is always nil.
  664. func (bw *bufferedWriter) Write(p []byte) (n int, err error) {
  665. return bw.buf.Write(p)
  666. }
  667. // WriteString write to the in-memory buffer. The error is always nil.
  668. func (bw *bufferedWriter) WriteString(p string) (n int, err error) {
  669. return bw.buf.WriteString(p)
  670. }
  671. // Reader provides read-access to the underlying buffer/file.
  672. func (bw *bufferedWriter) Reader() (io.Reader, error) {
  673. if bw.tmp == nil {
  674. return bytes.NewReader(bw.buf.Bytes()), nil
  675. }
  676. if err := bw.Flush(); err != nil {
  677. return nil, err
  678. }
  679. fi, err := bw.tmp.Stat()
  680. if err != nil {
  681. return nil, err
  682. }
  683. // os.File.ReadAt does not affect the cursor position and is safe to use here
  684. return io.NewSectionReader(bw.tmp, 0, fi.Size()), nil
  685. }
  686. // Sync will write the in-memory buffer to a temp file, if the in-memory
  687. // buffer has grown large enough. Any error will be returned.
  688. func (bw *bufferedWriter) Sync() (err error) {
  689. // Try to use local storage
  690. if bw.buf.Len() < StreamChunkSize {
  691. return nil
  692. }
  693. if bw.tmp == nil {
  694. bw.tmp, err = os.CreateTemp(os.TempDir(), "excelize-")
  695. if err != nil {
  696. // can not use local storage
  697. return nil
  698. }
  699. }
  700. return bw.Flush()
  701. }
  702. // Flush the entire in-memory buffer to the temp file, if a temp file is being
  703. // used.
  704. func (bw *bufferedWriter) Flush() error {
  705. if bw.tmp == nil {
  706. return nil
  707. }
  708. _, err := bw.buf.WriteTo(bw.tmp)
  709. if err != nil {
  710. return err
  711. }
  712. bw.buf.Reset()
  713. return nil
  714. }
  715. // Close the underlying temp file and reset the in-memory buffer.
  716. func (bw *bufferedWriter) Close() error {
  717. bw.buf.Reset()
  718. if bw.tmp == nil {
  719. return nil
  720. }
  721. defer os.Remove(bw.tmp.Name())
  722. return bw.tmp.Close()
  723. }