我正在尝试使用OpenXML从ASP.NET Web服务器写出Excel文件。我大约有2100条记录,大约需要20-30秒才能完成。有什么办法可以使它更快?从数据库中检索2100行需要一秒钟的时间。不知道为什么要在内存中处理它们会花费更多时间。
注意:ExcelWriter是我们的自定义类,但是其所有方法都直接来自此链接中的代码,http://msdn.microsoft.com/en-us/library/cc861607.aspx
public static MemoryStream CreateThingReport(List<Thing> things, MemoryStream template)
{
SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(template, true);
WorksheetPart workSheetPart = spreadsheet.WorkbookPart.WorksheetParts.First();
SharedStringTablePart sharedStringPart = spreadsheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
Cell cell = null;
int index = 0;
//create cell formatting for header text
Alignment wrappedAlignment = new Alignment { WrapText = true };
uint rowOffset = 2;
foreach (Thing t in things)
{
//Received Date
cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart);
index = ExcelWriter.InsertSharedStringItem(t.CreateDate.ToShortDateString(), sharedStringPart);
cell.CellValue = new CellValue(index.ToString());
cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString);
//Car Part Name
cell = ExcelWriter.InsertCellIntoWorksheet("B", rowOffset, workSheetPart);
index = ExcelWriter.InsertSharedStringItem(t.CarPart.Name, sharedStringPart);
cell.CellValue = new CellValue(index.ToString());
cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString);
rowOffset++;
}
workSheetPart.Worksheet.Save();
spreadsheet.WorkbookPart.Workbook.Save();
spreadsheet.Close();
return template;
因此,看起来MSDN社区文档中的某人遇到了类似的性能影响。下面的代码效率很低。有人建议使用哈希表。
对于我们的解决方案,我们只是完全删除了共享字符串的插入,下载时间从1:03秒变为0:03秒。
//Old: (1:03)
cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart);
index = ExcelWriter.InsertSharedStringItem(thing.CreateDate.ToShortDateString(), sharedStringPart);
cell.CellValue = new CellValue(index.ToString());
cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.SharedString);
//New: (0:03)
cell = ExcelWriter.InsertCellIntoWorksheet("A", rowOffset, workSheetPart);
cell.CellValue = new CellValue(thing.CreateDate.ToShortDateString());
cell.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.String);
MSDN Docs(缓慢的解决方案,他们应该使用哈希表)
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
// If the part does not contain a SharedStringTable, create one.
if (shareStringPart.SharedStringTable == null)
{
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
// Iterate through all the items in the SharedStringTable. If the text already exists, return its index.
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem and return its index.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
@ Internet
请注意,字符串数据类型实际上是用于公式的,对于文本,应使用InlineString。参见17.18.11 ST_CellType(单元格类型):
最大的麻烦是更多的Save()函数出现了循环
//Save data
shareStringPart.SharedStringTable.Save();
worksheetPart.Worksheet.Save();
对于500条记录,对我来说,它从10分钟变为1分钟。
@ kunjee
如果要提高性能,请先创建所有必需的对象,以便在每次调用此方法时都不会对其进行检查。这就是为什么SharedStringTable作为参数而不是部分作为参数传递的原因。
字典用于快速的索引查找,比for循环具有更好的性能。比哈希表快一点,因为它是强类型的,所以不需要装箱。无论如何,强类型输入是一个很大的好处。
private static int InsertSharedStringItem(string sharedString, SharedStringTable sharedStringTable, Dictionary<string, int> sharedStrings)
{
int sharedStringIndex;
if (!sharedStrings.TryGetValue(sharedString, out sharedStringIndex))
{
// The text does not exist in the part. Create the SharedStringItem now.
sharedStringTable.AppendChild(new SharedStringItem(new Text(sharedString)));
sharedStringIndex = sharedStrings.Count;
sharedStrings.Add(sharedString, sharedStringIndex);
}
return sharedStringIndex;
}
[正如Internet所提到的,他们应该使用哈希表,并且如zquanghoangz所建议,他们应该将Save()移出循环。
InlineString确实可以工作,但是当打开包含可修复但无意义的错误消息的生成的文件时,MS Excel会头疼。
static Cell AddCellWithSharedStringText(
[NotNull]string text,
[NotNull]Hashtable texts,
[NotNull]SharedStringTablePart shareStringPart
)
{
if (!texts.ContainsKey(text))
{
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text)));
texts[text] = texts.Count;
}
var idx = (int)texts[text];
Cell c1 = new Cell();
c1.DataType = CellValues.SharedString;
c1.CellValue = new CellValue(idx.ToString());
return c1;
}
此解决方案在[9880 x 66]网格上将导出时间从〜5分钟降低到6秒。