OpenXML - 继续写入下一个工作表(命中百万行)

OpenXML - continue writing to next worksheet (million row hit)

根据 Microsoft specifications.xlsx 文件的限制为 1,048,576 行每件作品sheet。理论上,正如我所见,这意味着我们甚至可以编写包含 200 万行的文件 - 在同一工作簿中有两个工作sheet。

我正在使用带有 SAX 方法的 OpenXML 包,这(恕我直言)仍然最适合编写大型 Excel 文件。我还扩展了我的解决方案,直接从 DataReader 写入 .xlsx 文件,以避免任何内存不足异常,因为我们的用户通常导出大量数据。

也就是说,当用户想要导出大于 1,048,576 行的数据时,我遇到了一个问题 - 因为 .xlsx 限制是(是的,他们实际上导出了这个数量)。

目前他们可以通过创建单独的 .xlsx 文件分两步完成,但我想知道是否可以在单个文件中完成?

对于代码部分:我设置了一个变量来检查行号 (row_number),如果它达到 100 万,那么应该创建一个新工作sheet,以便继续将数据从同一个 DataReader 写入下一个 sheet。

但是我在创建新的 sheet 时遇到了问题,因为我的数据是由 OpenXmlWriter 写入的,它已经为 sheet1 保存了一个 Sheetpart 实例.如我所见,如果我可以将 sheet2 的引用传递给 OpenXmlWriter:

,也许这会起作用
int row_number = 0;

using (var Excel_doc = SpreadsheetDocument.Create(file_path, SpreadsheetDocumentType.Workbook))
{
      var workbookPart = Excel_doc.AddWorkbookPart();

      Excel_doc.WorkbookPart.Workbook = new Workbook
      {
             Sheets = new Sheets()
      };

      var sheetPart = Excel_doc.WorkbookPart.AddNewPart<WorksheetPart>();

      //Add sheet
      Sheets sheets = Excel_doc.WorkbookPart.Workbook.GetFirstChild<Sheets>();
      string relationshipId = Excel_doc.WorkbookPart.GetIdOfPart(sheetPart);

      uint sheetId = 1;
      if (sheets.Elements<Sheet>().Count() > 0)
      {
         sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
      }

      Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = "Sheet " + sheetId };
      sheets.Append(sheet);

      using (var XML_write = OpenXmlWriter.Create(sheetPart))
      {
          XML_write.WriteStartElement(new Worksheet()); 
          XML_write.WriteStartElement(new SheetData());

          //Writing data using DataReader...
          using (OracleDataReader reader = cmd.ExecuteReader())
          {
              while (reader.Read())
              {
                 XML_write.WriteStartElement(new Row());

                 for (int i = 0; i < reader.FieldCount; i++)
                 {
                      row_number++;
                 }
                 XML_write.WriteEndElement(); //End of row

                //If 1 million row exceeded then proceed writing to next sheet - here is where I'm stucked
                if (row_number>1000000)
                {
                    sheetId +=1;   
                    Sheet sheet1 = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = "List " + sheetId };
                    sheets.Append(sheet1);

                    XML_write.WriteEndElement(); 
                    XML_write.WriteEndElement(); 
                    XML_write.WriteStartElement(new Worksheet()); 
                    XML_write.WriteStartElement(new SheetData());
                    row_number=0;
                 }
              }
          }
          XML_write.WriteEndElement(); 
          XML_write.WriteEndElement(); 
          XML_write.Close();
      }
}     

在写入 .xlsx 文件时,此代码因错误而终止:

Token StartElement in state EndRootElement would result in an invalid XML document. Make sure that the ConformanceLevel setting is set to ConformanceLevel.Fragment or ConformanceLevel.Auto if you want to write an XML fragment

如果有人对此有解决方案,或者建议让它发挥作用,我会非常高兴。

P.S.: 一些解决方案中已经存在类似的东西 - 例如Toad for Oracle,在达到最大 65k 行后在多个 sheet 上导出到 .xls 文件。所以大概可以做到。

看来您基本上需要交换循环的顺序。打开你的连接,然后创建一个 sheet 并使用它直到计数器达到 100 万,然后关闭它并创建另一个。

这是一些基本的伪代码。

count = 0
sheet = new
writer = new writer(sheet)
using (reader)
{
    foreach (row in reader)
    {
        if (count % 1,000,000 == 0)
        {
            writer.close
            sheet = new
            writer = new writer(sheet)
        }
        writer.write(reader.read)
        count++
    }
}
writer.close