【发布时间】:2017-02-11 13:50:41
【问题描述】:
当我尝试使用CsvHelper 将大量数据(包含 300 000 行及更多行的列表)写入内存流时,它会抛出异常 "System.IO.IOException: Stream was too long 。”。
数据类相当大,有大约 30 个属性,因此文件中的每条记录将有大约 30 列。
这是引发异常的实际编写代码(顺便说一下,此代码基于 CsvHelper lib 的作者that 的回答):
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream, encoding ?? Encoding.ASCII))
{
var csvWriter = new CsvWriter(streamWriter, GetConfiguration(delimiter, mappingClassType, mappingActions));
csvWriter.WriteRecords(data); //data is IEnumerable<T> and has more than 300k records
streamWriter.Flush();
return memoryStream.ToArray();
}
}
然后我将结果字节数组保存到文件中。
File.WriteAllBytes(filePath, resultedBytesArray);
请注意,当我将 100 000 条记录写入文件时,相同的代码运行良好(在这种情况下,文件的大小约为 1GB)。顺便说一句,我的目标是写入超过 600 000 条数据记录。
这是与此问题相关的堆栈跟踪的相关部分。
Stream was too long.|System.IO.IOException: Stream was too long.
at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
at System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count)
at CsvHelper.CsvWriter.NextRecord() in C:\Users\Josh\Projects\CsvHelper\src\CsvHelper\CsvWriter.cs:line 290
at CsvHelper.CsvWriter.WriteRecords(IEnumerable records) in C:\Users\Josh\Projects\CsvHelper\src\CsvHelper\CsvWriter.cs:line 490
at FileExport.Csv.CsvDocument.Create[T](IEnumerable`1 data, String delimiter, Encoding encoding, Type mappingClassType, IDictionary`2 mappingActions) in d:\Dev\DrugDevExport\FileExport\Csv\CsvDocument.cs:line 33
就我而言,实现我的目标并避免该问题的基本方法是将我的书面数据列表分成几个部分,然后将它们连接在一起,但可能有任何非常明显和简单的解决方案没有重要的代码重构(如增加默认流/缓冲区大小等)?
另外请记住,我还应用了两种可能的解决方案,以防止“内存不足”对象异常。
- 摆脱了对象的 2GB 限制(来自这里 https://stackoverflow.com/a/20912869) 是的,我在具有 32GB RAM 的 x64 操作系统上运行。
- 在构建设置部分设置 x64“平台目标”(来自此处https://stackoverflow.com/a/22592876)
提前致谢。
【问题讨论】:
-
为什么要写入 MemoryStream?您是否需要将流完全放在内存中?您谈论文件,但使用 MemoryStream... 用 FileStream 替换它,看看会发生什么...
-
您是否尝试过读取有限数量的数据并将其循环写入流?即不是一次全部。您也许可以尝试与此帖子 stackoverflow.com/questions/2819081/… 类似的分块方法
-
@PaulZahra,我在我的问题中提到了这一点,这种方式(通过拆分整组数据)很可能会起作用,现在它适用于 100k 数据记录,但是否存在没有分裂的任何其他解决方案?
-
@ArtyomPranovich 我认为分块更合乎逻辑/安全/未来证明,否则您过于依赖机器......您可以尝试定义自己的缓冲区(给它一个大小)但是您很可能会遇到内存必须连续的问题)ps根据“那个”帖子:当你离开它的 using 语句时,streamWriter 将被自动刷新,这很好,因为你在 using 内部返回(所以删除你的刷新)
-
我同意@spender - 你似乎无缘无故地跳过了很多圈。您将列表写入一个流,然后将整个流读入一个数组,然后将该数组写入第二个流。只需将其直接写入第二个流即可。实际上,您正在为内存中的相同数据创建三种不同的表示形式(列表、MemoryStream 的底层存储以及不仅仅是对 MemoryStream 缓冲区的引用的 byte[])。我认为非常明显的解决方案是不在内存中存储三次大数据。
标签: c# .net memory-management csvhelper