直接使用 OpenXML SDK 而不是通过 Excel 的自动化模型要复杂得多且容易出错。因此,我建议为此使用库;特别是如果您的任务变得更复杂(例如http://excelpackage.codeplex.com/)。
编辑:可以在这里找到使用 ExcelPackage 进行类似操作的示例:http ://excelpackage.codeplex.com/wikipage?title=Using%20a%20template%20to%20create%20an%20Excel%20spreadsheet虽然我不知道与使用原始 SDK 相比,期望的性能如何,我猜 ExcelPackage 无论如何都在内部使用 SDK,因此它可能会产生一些开销。可能只有针对您的具体场景的测量才能在这里提供明确的答案。
如果您想坚持使用 SDK,尽管这是将字符串插入 Excel 工作簿的示例:
string filePath = "workbook.xlsx";
string sheetName = "Sheet1";
uint startRow = 9;
string columnName = "C";
string[] data = new string[] { "A", "B", "C" };
using (var spreadsheetDocument = SpreadsheetDocument.Open(filePath, true))
{
// Find the Id of the worksheet in question
var sheet = spreadsheetDocument.WorkbookPart.Workbook
.Sheets.Elements<Sheet>()
.Where(s => s.Name == sheetName).First();
var sheetReferenceId = sheet.Id;
// Map the Id to the worksheet part
WorksheetPart worksheetPart = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(sheetReferenceId);
var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
// Inset the data at the given location
for (uint i = 0; i < data.Length; i++)
{
uint rowNumber = startRow + i;
// Find the XML entry for row i
var row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowNumber).FirstOrDefault();
if (row == null)
{
// Row does not exist yet, create it
row = new Row();
row.RowIndex = rowNumber;
// Insert the row at its correct sequential position
Row rowAfter = null;
foreach (Row otherRow in sheetData.Elements<Row>())
{
if (otherRow.RowIndex > row.RowIndex)
{
rowAfter = otherRow;
break;
}
}
if (rowAfter == null)
// New row is the last row in the sheet
sheetData.Append(row);
else
sheetData.InsertBefore(row, rowAfter);
}
// CellReferences in OpenXML are "normal" Excel cell references, e.g. D15
string cellReference = columnName + rowNumber.ToString();
// Find cell in row
var cell = row.Elements<Cell>()
.Where(c => c.CellReference == cellReference)
.FirstOrDefault();
if (cell == null)
{
// Cell does not exist yet, create it
cell = new Cell();
cell.CellReference = new StringValue(cellReference);
// The cell must be in the correct position (e.g. column B after A)
// Note: AA must be after Z, so a normal string compare is not sufficient
Cell cellAfter = null;
foreach (Cell otherCell in row.Elements<Cell>())
{
// This is ugly, but somehow the row number must be stripped from the
// cell reference for comparison
string otherCellColumn = otherCell.CellReference.Value;
otherCellColumn = otherCellColumn.Remove(otherCellColumn.Length - rowNumber.ToString().Length);
// Now compare first to length and then alphabetically
if (otherCellColumn.Length > columnName.Length ||
string.Compare(otherCellColumn, columnName, true) > 0)
{
cellAfter = otherCell;
break;
}
}
if (cellAfter == null)
// New cell is last cell in row
row.Append(cell);
else
row.InsertBefore(cell, cellAfter);
}
// Note: This is the most simple approach.
// Normally Excel itself will store the string as a SharedString,
// which is more difficult to implement. The only drawback of using
// this approach though, is that the cell might have been the only
// reference to its shared string value, which is not deleted from the
// list here.
cell.DataType = CellValues.String;
cell.CellValue = new CellValue(data[i]);
}
}
请注意,这个例子并不完美,因为它根本没有考虑复杂的场景(例如样式、打印边距、合并单元格......)。对于生产用途,您可能希望将某些功能提取到方法中(例如在正确的位置插入行或单元格),甚至单独提取到类中(例如,关于按正确顺序比较单元格引用的部分)。
编辑:使用 SDK 而不是通过自动化模型的性能要好得多(这可能是 SDK 的第二个巨大优势,第一个是您不需要安装 Excel)。如果您仍然看到性能瓶颈,这里有一些改进的想法:
- 确保已经在模板文档中创建了单元格(例如,在每个使用 Excel 中插入一个 0)。这可以节省您创建新单元的工作量。
- 确保模板中不存在任何单元格,然后按顺序创建所有单元格(节省您至少为行找到正确插入位置的工作)。
- 直接操作 XML,难度更大,需要更多了解 OpenXML 的内部工作原理。对于相当统一的文档,这可能是可以接受的。