stream.go 13 KB

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