excel - 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
,它已经拥有Sheetpart
sheet1 的实例。如我所见,如果我可以将 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 文件。所以大概是可以做到的。
解决方案
基本上,您似乎需要交换循环的顺序。打开您的连接,然后创建一个工作表并使用它,直到计数器达到 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
推荐阅读
- laravel - Laravel - 多对多返回过滤的子记录
- google-sheets - 谷歌表格将包含多个数据的多列转换为行中的详细信息
- python - 是否有一个函数来规范化字符串并将它们转换为整数/浮点数?
- python-3.x - 迭代列表字典中的任意两个元素
- c# - MassTransit 中是否有对命名空间消费者的开箱即用支持?
- python - 如何重用 wtform 在数据库中更新和添加记录
- python - 在 for 循环中附加多维列表,其中索引随循环迭代而变化
- python - 如何使用 PySpark 读取目录下的 Parquet 文件?
- android - 如何从 RecyclerView 保存 CheckBox 状态?
- java - 未找到 JNativeHook 类