将超过 30 万条记录导出到 excel 生成 System.OutOfMemoryException
exporting over 300k records to excel generates System.OutOfMemoryException
我正在尝试将超过 30 万条记录导出到 excel,但我真的不想为此使用任何 dll。
我创建了一个模拟该问题的示例应用程序。下面是 class,它具有生成虚拟数据 table 和将数据 table 导出到 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", ",590", ",432",
",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");
这是我得到的错误。
Exception of type 'System.OutOfMemoryException' was thrown.
它发生在下一行。
string sTable = sTableStart + sTHead + sTableData.ToString() + sTableEnd;
有时,它也有效。如果不是模拟,您可以尝试将循环计数从 300k 增加到 500k。
我正在使用 excel 2007/2010。
你在那段代码中绝对是在浪费内存。尽管您确实利用了 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 实例的内部缓冲区。
我选择了没有任何 StringBuilder 的解决方案。只需直接写出到流中:
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;
}
这不会做更多的分配,甚至应该适用于更大的数据表。
我正在尝试将超过 30 万条记录导出到 excel,但我真的不想为此使用任何 dll。
我创建了一个模拟该问题的示例应用程序。下面是 class,它具有生成虚拟数据 table 和将数据 table 导出到 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", ",590", ",432",
",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");
这是我得到的错误。
Exception of type 'System.OutOfMemoryException' was thrown.
它发生在下一行。
string sTable = sTableStart + sTHead + sTableData.ToString() + sTableEnd;
有时,它也有效。如果不是模拟,您可以尝试将循环计数从 300k 增加到 500k。
我正在使用 excel 2007/2010。
你在那段代码中绝对是在浪费内存。尽管您确实利用了 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 实例的内部缓冲区。
我选择了没有任何 StringBuilder 的解决方案。只需直接写出到流中:
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;
}
这不会做更多的分配,甚至应该适用于更大的数据表。