【发布时间】:2016-05-30 13:49:19
【问题描述】:
我需要将大量数据从数据库表导出到 excel (xls/xlsx) 文件。 它可能很容易达到 1000 万行甚至更多。
我需要不需要安装 Office 的开源解决方案(SpreadsheetGear 和互操作解决方案不适合我)。
我正在检查两个库:OpenXML SDK 和 EPPlus。
对于 OpenXML SDK,我找到了这个方法:
private static void Write(string fileName, int numRows, int numCols)
{
using (var spreadsheetDocument = SpreadsheetDocument.Open(fileName, true))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
string origninalSheetId = workbookPart.GetIdOfPart(worksheetPart);
WorksheetPart replacementPart = workbookPart.AddNewPart<WorksheetPart>();
string replacementPartId = workbookPart.GetIdOfPart(replacementPart);
using (OpenXmlReader reader = OpenXmlReader.Create(worksheetPart))
{
using (OpenXmlWriter writer = OpenXmlWriter.Create(replacementPart))
{
Row row = new Row();
Cell cell = new Cell();
//CellFormula cellFormula = new CellFormula();
//cellFormula.CalculateCell = true;
//cellFormula.Text = "RAND()";
//cell.Append(cellFormula);
CellValue cellValue = new CellValue("val val");
cell.Append(cellValue);
while (reader.Read())
{
if (reader.ElementType == typeof(SheetData))
{
if (reader.IsEndElement)
continue;
writer.WriteStartElement(new SheetData());
for (int rowNumber = 0; rowNumber < numRows; rowNumber++)
{
writer.WriteStartElement(row);
for (int col = 0; col < numCols; col++)
{
writer.WriteElement(cell);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
else
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
}
}
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().First(s => s.Id.Value.Equals(origninalSheetId));
sheet.Id.Value = replacementPartId;
workbookPart.DeletePart(worksheetPart);
}
}
但它会引发Out of memory 异常。
我需要batch oriented 方法并能够将append 数据添加到excel 文档的末尾。
不幸的是,我没有找到如何使用OpenXML SDK 追加行。
另外,我用LoadFromCollection 方法检查了EPPlus soluion。
它确实支持IDataReader 和LoadFromDataReader,但我当时在代码中没有数据读取器。
问题:有没有办法使用某种编写器将数据附加到现有的工作表 xls/xlsx 文件中?喜欢OpenXMLWriter 中的OpenXML SDK。
UPD。 Excel 显然不支持 1000 万行。让我们坚持 1m 行并丢失列而不会出现内存不足的异常。
UPD。添加了 EPPlus 示例。 6 分钟内导出 20 万行,最多占用 1GB 内存。
private const string TempFile = @"C:\Users\vnechyp\Desktop\temp.xlsx";
private static void EPPlusExport()
{
var random = new Random();
var dt = new System.Data.DataTable();
for (int i = 0; i < 15; i++)
{
dt.Columns.Add($"column_{i}");
}
var values = Enumerable.Range(0, 15).Select(val => random.Next().ToString()).ToArray();
for (int i = 0; i < 10000; i++)
{
dt.Rows.Add(values);
}
using (ExcelPackage excelPackage = new ExcelPackage())
{
var workSheet = excelPackage.Workbook.Worksheets.Add("sheet");
workSheet.Cells[1, 1].LoadFromDataTable(dt, true);
excelPackage.SaveAs(new FileInfo(TempFile));
}
for (int i = 1; i < 50; i++)
{
Console.WriteLine($"Iteration: {i}");
var updateRow = i*10000;
Console.WriteLine($"Rows: {updateRow}");
FileInfo existingFile = new FileInfo(TempFile);
using (ExcelPackage excelPackage = new ExcelPackage(existingFile))
{
// get the first worksheet in the workbook
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
worksheet.Cells[updateRow, 1].LoadFromDataTable(dt, true);
excelPackage.SaveAs(new FileInfo(TempFile));
}
}
}
【问题讨论】:
-
我投票决定以“寻找教程”的形式结束这个。我已经多次使用 EPPlus 进行这种类型的操作。他们的网站有很多继续写入现有工作表的示例。您知道您的问题是您有太多数据无法保存在内存中并一次转储。继续查看文档即可。
-
Excel 不适合这种大小的数据集。
-
@krillgar 谢谢你,检查文档。上次我什么都没发现
-
@makambi 如果他们想要一个很好的 CSV - 它可以导入 Access 或其他数据库,这很好。如果他们想要一个 .xls 或 .xlsx 文件,那么您的工作就是巧妙地告诉他们他们错了,并且陷入了痛苦的世界。这不是一个可行的解决方案,句号。 Excel 可以链接到 Access 以查看数据透视表或其他任何内容中的数据,但是当您开始拥有这么多数据时,它必须进入数据库。工作表根本不会削减它。这就像试图将集装箱装载到皮卡车上 - 一切都会损坏。
-
@J... 关于 Excel 不适合这项工作的工具是正确的。也就是说,看看我的answer here,它展示了如何使用 SAX 方法使用 OpenXml SDK。这会在我的机器上大约 22 秒内写入 200k 行(根据您的 EPPus 测试)。
标签: c# excel openxml-sdk epplus