首页 > 解决方案 > OpenXML - 继续写入下一个工作表(百万行命中)

问题描述

根据 Microsoft规范.xlsx 文件的每个工作表的行数限制为 1,048,576 行。理论上,正如我所看到的,这意味着我们可以编写包含 200 万行的文件——在同一个工作簿中使用两个工作表。

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

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

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

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

但是,我在创建新工作表时遇到了问题,因为我的数据是由 写入的OpenXmlWriter,它已经拥有Sheetpartsheet1 的实例。如我所见,如果我可以将 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文件时,此代码以错误终止:

处于 EndRootElement 状态的令牌 StartElement 将导致无效的 XML 文档。如果要编写 XML 片段,请确保 ConformanceLevel 设置设置为 ConformanceLevel.Fragment 或 ConformanceLevel.Auto

如果有人对此有解决方案或建议使其发挥作用,我将非常高兴。

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

标签: excelopenxmldatareader

解决方案


基本上,您似乎需要交换循环的顺序。打开您的连接,然后创建一个工作表并使用它,直到计数器达到 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

推荐阅读