2

将大量数据导出到字符串(csv 格式)时,出现 OutOfMemoryException。解决这个问题的最佳方法是什么?字符串返回到 Flex 应用程序。

我要做的是将 csv 导出到服务器磁盘并将 URL 返回给 Flex。像这样,我可以刷新写入磁盘的流。

更新:

使用 StringBuilder 构建字符串:

StringBuilder stringbuilder = new StringBuilder();
string delimiter = ";";
bool showUserData = true;

// Get the data from the sessionwarehouse
List<DwhSessionDto> collection 
     =_dwhSessionRepository.GetByTreeStructureId(treeStructureId);

// ADD THE HEADERS
stringbuilder.Append("UserId" + delimiter);
if (showUserData)
{
    stringbuilder.Append("FirstName" + delimiter);
    stringbuilder.Append("LastName" + delimiter);
}
stringbuilder.Append("SessionId" + delimiter);
stringbuilder.Append("TreeStructureId" + delimiter);
stringbuilder.Append("Name" + delimiter);
stringbuilder.Append("Score" + delimiter);
stringbuilder.Append("MaximumScore" + delimiter);
stringbuilder.Append("MinimumScore" + delimiter);
stringbuilder.Append("ReducedScore" + delimiter);
stringbuilder.Append("ReducedMaximumScore" + delimiter);
stringbuilder.Append("Duration" + delimiter);
stringbuilder.AppendLine("Category" + delimiter);

foreach (var dwhSessionDto in collection)
{
    stringbuilder.Append(
        getPackageItemsInCsvFromDwhSessionDto(
            dwhSessionDto, delimiter, showUserData));
}

return stringbuilder.ToString();

字符串被发送回 Flex,如下所示:

var contentType = "text/csv";
string result = exportSessionService.ExportPackage(treeStructureId);
// Write the export to the response
_context.Response.ContentType = contentType;
_context.Response.AddHeader("content-disposition", 
    String.Format("attachment; filename={0}", treeStructureId + ".csv"));

// do not Omit the Vary star so the caching at the client side will be disabled
_context.Response.Cache.SetOmitVaryStar(false);
_context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

if (!String.IsNullOrEmpty(result))
{
    _context.Response.Output.Write(result);
    _context.Response.Output.Close();
}
else
{
    _context.Response.Output.Write("No logs");
}
4

5 回答 5

6

您的 CSV 字符串增长到超过 80000 个字节并最终出现在 LargeObjectHeap 上。LOH 不像其他代那样垃圾收集,并且会随着时间的推移而碎片化,例如在向您的服务器发出许多请求之后,或者如果您使用字符串连接(淘气!)来构建这个 csv 数据。结果是您的程序保留了比实际使用更多的内存,并引发了 OutOfMemory 异常。

在这种情况下,解决方法是将 csv 数据直接写入响应流,而不是字符串变量甚至 StringBuilder。这不仅可以避免大对象堆,而且可以降低整体内存使用量,并开始更快地将数据推送给用户。

于 2010-09-17T14:15:20.897 回答
5

使用 StringBuilder 创建字符串并使用 HttpContext.Reponse.Output.Write(result) 将其传回 Flex 应用程序;– 列文·卡登

所以你基本上在做的是:

StringBuilder str = new StringBuilder();
while(whatever)
{
    str.Append(thisNextLineIGuess);
}
Response.Output.Write(str.ToString());

正确的?这可能不是您的确切代码,但听起来像是一个很好的近似值。

那么解决方案很简单:

while(whatever)
{
    Response.Output.Write(thisNextLineIGuess);
}

流式传输通常比缓冲整个内容并将其作为一个块发送出去要好。

于 2010-09-17T14:36:27.260 回答
5

我要做的是将 csv 导出到服务器磁盘

听起来是正确的想法。但是您是先创建完整的字符串吗?如果你逐行写你不应该得到OOM

编辑,看到代码后

你只是在使用StringBuilder.Append(x),所以你可以稍微重构你的代码来替换它_context.Response.Output.Write(x)。这可能会牺牲一点你们的分离,但这是为了一个好的事业。

如果您想继续使用 StringBuilder,如果您可以估计结果大小,将会有很大帮助。添加充足的边距并使用

   StringBuilder stringbuilder = new StringBuilder(estimate);

这节省了 StringBuilder 的增长(复制),并减少了 LOH 上的内存使用和碎片。

于 2010-09-17T14:13:38.323 回答
3

对我来说,最好创建一个通用处理程序页面(ashx),并将所有数据直接发送到 flex。

直接进入流...

这是一篇关于这个问题的好文章

于 2010-09-17T14:13:20.880 回答
0

首先,增加 RAM 的成本是多少?如今,硬件比程序员便宜。

没有更多内容可做,更具体的建议有点花招。但是您希望避免将 CSV 实际构建为字符串,而是将其从数据源流式传输到磁盘,而不是将整个内容保存在内存中。

于 2010-09-17T14:13:32.833 回答