2016-04-23 2 views
0

Я пытаюсь экспортировать более 300 тыс. Записей в Excel, и я действительно не хочу использовать DLL для этого.Экспорт более 300 тыс. Записей в excel генерирует System.OutOfMemoryException

Я создал образец приложения, которое имитирует проблему. Ниже приведен класс, который имеет методы генерации фиктивных данных и экспорта таблицы данных в excel.

public class ExcelCreator 
    { 
     /// <summary> 
     /// Create one Excel-XML-Document with SpreadsheetML from a DataTable 
     /// </summary> 
     /// <param name="dataSource">Datasource which would be exported in Excel</param> 
     /// <param name="fileName">Name of exported file</param> 


     public static DataTable GiveDummyDataTable() 
     { 
      DataTable dt = new DataTable(); 
      dt.Columns.Add("abc"); 
      dt.Columns.Add("bcd"); 
      dt.Columns.Add("dfd"); 
      dt.Columns.Add("wer"); 
      dt.Columns.Add("werw"); 
      dt.Columns.Add("rete"); 
      dt.Columns.Add("lkj"); 
      dt.Columns.Add("ert"); 
      dt.Columns.Add("poi"); 
      dt.Columns.Add("wers"); 
      dt.Columns.Add("mnb"); 
      dt.Columns.Add("oiwu"); 
      dt.Columns.Add("qwe"); 
      dt.Columns.Add("uio"); 
      for (int i = 0; i < 500000; i++) 
      { 
       dt.Rows.Add(new object[] { "babo", 120, "poi", "123 3428749020", 35, "6.000", "$24,590", "$13,432", 
      "$12,659", "12/13/21", "1/30/27", 55, "sonumonu", "wer"}); 
      } 


      return dt; 
     } 

     public static bool sonaKaExcelBanao(DataTable dt, string filename) 
     { 
      try 
      { 
       string sTableStart = @"<HTML><BODY><TABLE Border=1>"; 
       string sTableEnd = @"</TABLE></BODY></HTML>"; 
       string sTHead = "<TR>"; 
       StringBuilder sTableData = new StringBuilder(); 
       foreach (DataColumn col in dt.Columns) 
       { 
        sTHead += @"<TH>" + col.ColumnName + @"</TH>"; 
       } 
       sTHead += @"</TR>"; 
       foreach (DataRow sonurow in dt.Rows) 
       { 
        sTableData.Append(@"<TR>"); 
        for (int i = 0; i < dt.Columns.Count; i++) 
        { 
         sTableData.Append(@"<TD>" + sonurow[i].ToString() + @"</TD>"); 
        } 
        sTableData.Append(@"</TR>"); 
       } 
       string sTable = sTableStart + sTHead + sTableData.ToString() + sTableEnd; 
       System.IO.StreamWriter oExcelWriter = System.IO.File.CreateText(filename); 
       oExcelWriter.WriteLine(sTable); 
       oExcelWriter.Close(); 
       return true; 
      } 
      catch 
      { 
       return false; 
      } 
     } 
    } 

Ниже приведено то, как я называю эти методы.

DataTable dt = ExcelCreator.GiveDummyDataTable();   
      ExcelCreator.sonaKaExcelBanao(dt, @"c:\chunchuntaiyar.xls"); 

И вот ошибка, которую я получаю.

Исключение типа 'System.OutOfMemoryException' было выброшено.

, и это происходит в нижней строке.

string sTable = sTableStart + sTHead + sTableData.ToString() + sTableEnd; 

иногда, он также работает. Если он не имитирует, вы можете попытаться увеличить количество циклов до 500 тыс. С 300 тыс.

Я использую Excel 2007/2010.

+0

Я предполагаю, что «лаки» - это слово для 100000? – Wormbo

+0

@Wormbo: да ... – Arya220

ответ

0

Вы абсолютно теряете память в этом фрагменте кода. Несмотря на то, что вы использовали StringBuilder, что, вероятно, предотвратило бы, что раньше у вас не хватило памяти, вы упустили пару возможностей использовать StringBuilder еще эффективнее. Например, вы могли бы использовать его для полного наращивания.

Возьмите этот пример из вашего кода:

sTHead += @"<TH>" + col.ColumnName + @"</TH>"; 

Каждый + на этой линии будет создавать новую строку для хранения результата. Вместо этого используйте StringBuilder.

Это где у вас уже есть StringBuilder на месте, но ваша строка кода еще выделяет дополнительные строки:

sTableData.Append(@"<TD>" + sonurow[i].ToString() + @"</TD>"); 

Вы могли бы использовать AppendFormat там:

sTableData.AppendFormat(@"<TD>{0}</TD>", sonurow[i]); 

так струны будет эффективно копируется во внутренний буфер экземпляра StringBuilder.

Я выбрал решение без каких-либо StringBuilders. Просто напишите непосредственно в поток:

public static bool ExcelExport(DataTable dt, string filename) 
{ 
    try 
    { 
     // using makes sure the streamwriter gets closed and disposed 
     using (StreamWriter oExcelWriter = File.CreateText(filename)) 
     { 
      // leadin 
      oExcelWriter.Write(@"<HTML><BODY><TABLE Border=1>"); 
      //header 
      oExcelWriter.Write("<TR>"); 
      foreach (DataColumn col in dt.Columns) 
      { 
       oExcelWriter.Write(@"<TH>"); 
       oExcelWriter.Write(col.ColumnName); 
       oExcelWriter.Write(@"</TH>"); 
      } 
      oExcelWriter.Write("</TR>"); 
      // body 
      foreach (DataRow sonurow in dt.Rows) 
      { 
       oExcelWriter.Write(@"<TR>"); 
       for (int i = 0; i < dt.Columns.Count; i++) 
       { 
        oExcelWriter.Write(@"<TD>"); 
        oExcelWriter.Write(sonurow[i]); // calls ToString in the overload 
        oExcelWriter.Write(@"</TD>"); 
       } 
       oExcelWriter.Write(@"</TR>"); 
      } 
      // leadout 
      oExcelWriter.WriteLine(@"</TABLE></BODY></HTML>"); 
     } 
    } 
    catch(Exception exp) 
    { 
      Trace.WriteLine(exp.Message); 
      return false; 
    } 
    return true; 
} 

Это не будет делать гораздо больше ассигнований и должны работать даже для больших DataTables.