9

我正在尝试使用 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;
4

5 回答 5

8

所以看起来 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;
 }  
于 2012-05-11T15:46:15.753 回答
5

@互联网

请注意,String 数据类型实际上是用于公式的,对于文本应使用 InlineString。请参阅 17.18.11 ST_CellType(小区类型):

  • inlineStr(内联字符串)- 包含(内联)富字符串的单元格,即不在共享字符串表中的单元格。如果使用此单元格类型,则单元格值在 is 元素中,而不是单元格中的 v 元素(c 元素)。
  • str (String) - 包含公式字符串的单元格。
于 2014-05-19T20:36:57.553 回答
4

最大的改进是更多的 Save() 函数在循环之外

 //Save data
        shareStringPart.SharedStringTable.Save();
        worksheetPart.Worksheet.Save();

对于 500 条记录,对我来说,它从 10 分钟变为 1 分钟。

于 2015-02-12T10:43:02.773 回答
2

@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;
}
于 2014-05-19T20:30:26.777 回答
0

正如 Internet 所提到的,他们应该使用 Hashtable,并且按照 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 秒。

于 2019-10-25T07:12:29.143 回答