2016-10-03 5 views
1

CsvHelper을 사용하여 매우 많은 양의 데이터 (300 000 행 이상 목록)를 메모리 스트림에 쓰려고 할 때 "System.IO.IOException 예외가 발생합니다. : 스트림이 너무 깁니다. ".많은 양의 데이터를 쓰지 못했습니다.

데이터 클래스는 크기가 커서 ~ 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(); 
    } 
} 

는 다음 나는 파일에 결과를 바이트의 배열을 저장합니다. 나는 (이 경우 파일이 약 1GB 크기가) 파일 100 개 000 기록을 쓸 때 동일한 코드가 잘 작동 것을
File.WriteAllBytes(filePath, resultedBytesArray); 

있습니다

. 그건 그렇고, 내 목표는 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 

지금까지 내가 내 목표를 달성하고 문제가 몇 가지 부분에 최대 기록 된 데이터의 내 목록을 분할하고 그들을 함께 연결하는 것입니다 피할 수있는 기본적인 방법을 걱정 될 수 있지만 거기 어떤 꽤 중요한 코드 리팩토링 (예 : 기본 스트림/버퍼 크기 증가 등)없이 확실하고 쉬운 솔루션?

"메모리 부족"개체 예외를 방지하기 위해 가능한 두 가지 솔루션을 적용했습니다.

감사합니다 (여기 https://stackoverflow.com/a/22592876에서) 빌드 설정 섹션에서 64 "플랫폼 대상"을 설정합니다.

+1

왜 당신은 MemoryStream을에 쓰고있다? 스트림을 전체적으로 메모리에 저장해야합니까? 당신은 파일에 대해 이야기하지만 MemoryStream을 사용합니다 ... FileStream으로 바꾸고 무슨 일이 일어나는 지 봅니다 ... – spender

+0

제한된 양의 데이터를 읽고 루프에서 스트림에 쓰려고 했습니까? 즉 모든 것을 한꺼번에 볼 수는 없습니다. 아마도이 게시물에 chunking하는 비슷한 방법을 시도해 볼 수 있습니다. http://stackoverflow.com/questions/2819081/memorystream-and-large-object-heap –

+0

@PaulZahra, 내 질문에 대해 언급했습니다. 데이터 전체를 나눠서) 작동 할 가능성이 높으며 100k 데이터 레코드로 작동하지만 분할하지 않고 다른 솔루션이 있습니까? –

답변

1

많은 질문에 아래에 언급 한 것처럼 Spender 덕분에 MemoryStream을 FileStream으로 대체하고 파일에 직접 데이터를 쓰는 방법으로 문제가 해결되었습니다.

필자의 경우 MemoryStream에 데이터를 쓰고 아무 이유없이 파일에 다시 복사하는 것은 절대적으로 쓸모가 없었습니다. 그 사실에 내 눈을 열어 주셔서 감사합니다 him 다시.

아래 내 고정 코드입니다.

using (var fileStream = File.Create(path)) 
{ 
    using (var streamWriter = new StreamWriter(fileStream, encoding ?? Encoding.ASCII)) 
    { 
     var csvWriter = new CsvWriter(streamWriter, GetConfiguration(delimiter, mappingClassType, mappingActions)); 
     csvWriter.WriteRecords(data); 
    } 
} 

이제 모든 입력 데이터와 함께 작동합니다.

1

당신은 당신의 자신의 MemoryStream을을 작성하여 2기가바이트의 한계를 해결할 수 있습니다 :

class HugeMemoryStream : Stream 
    { 
     #region Fields 

     private const int PAGE_SIZE = 1024000; 
     private const int ALLOC_STEP = 1024; 

     private byte[][] _streamBuffers; 

     private int _pageCount = 0; 
     private long _allocatedBytes = 0; 

     private long _position = 0; 
     private long _length = 0; 

     #endregion Fields 

     #region Internals 

     private int GetPageCount(long length) 
     { 
      int pageCount = (int)(length/PAGE_SIZE) + 1; 

      if ((length % PAGE_SIZE) == 0) 
       pageCount--; 

      return pageCount; 
     } 

     private void ExtendPages() 
     { 
      if (_streamBuffers == null) 
      { 
       _streamBuffers = new byte[ALLOC_STEP][]; 
      } 
      else 
      { 
       byte[][] streamBuffers = new byte[_streamBuffers.Length + ALLOC_STEP][]; 

       Array.Copy(_streamBuffers, streamBuffers, _streamBuffers.Length); 

       _streamBuffers = streamBuffers; 
      } 

      _pageCount = _streamBuffers.Length; 
     } 

     private void AllocSpaceIfNeeded(long value) 
     { 
      if (value < 0) 
       throw new InvalidOperationException("AllocSpaceIfNeeded < 0"); 

      if (value == 0) 
       return; 

      int currentPageCount = GetPageCount(_allocatedBytes); 
      int neededPageCount = GetPageCount(value); 

      while (currentPageCount < neededPageCount) 
      { 
       if (currentPageCount == _pageCount) 
        ExtendPages(); 

       _streamBuffers[currentPageCount++] = new byte[PAGE_SIZE]; 
      } 

      _allocatedBytes = (long)currentPageCount * PAGE_SIZE; 

      value = Math.Max(value, _length); 

      if (_position > (_length = value)) 
       _position = _length; 
     } 

     #endregion Internals 

     #region Stream 

     public override bool CanRead => true; 

     public override bool CanSeek => true; 

     public override bool CanWrite => true; 

     public override long Length => _length; 

     public override long Position 
     { 
      get { return _position; } 
      set 
      { 
       if (value > _length) 
        throw new InvalidOperationException("Position > Length"); 
       else if (value < 0) 
        throw new InvalidOperationException("Position < 0"); 
       else 
        _position = value; 
      } 
     } 

     public override void Flush() { } 

     public override int Read(byte[] buffer, int offset, int count) 
     { 
      int currentPage = (int)(_position/PAGE_SIZE); 
      int currentOffset = (int)(_position % PAGE_SIZE); 
      int currentLength = PAGE_SIZE - currentOffset; 

      long startPosition = _position; 

      if (startPosition + count > _length) 
       count = (int)(_length - startPosition); 

      while (count != 0 && _position < _length) 
      { 
       if (currentLength > count) 
        currentLength = count; 

       Array.Copy(_streamBuffers[currentPage++], currentOffset, buffer, offset, currentLength); 

       offset += currentLength; 
       _position += currentLength; 
       count -= currentLength; 

       currentOffset = 0; 
       currentLength = PAGE_SIZE; 
      } 

      return (int)(_position - startPosition); 
     } 

     public override long Seek(long offset, SeekOrigin origin) 
     { 
      switch (origin) 
      { 
       case SeekOrigin.Begin: 
        break; 

       case SeekOrigin.Current: 
        offset += _position; 
        break; 

       case SeekOrigin.End: 
        offset = _length - offset; 
        break; 

       default: 
        throw new ArgumentOutOfRangeException("origin"); 
      } 

      return Position = offset; 
     } 

     public override void SetLength(long value) 
     { 
      if (value < 0) 
       throw new InvalidOperationException("SetLength < 0"); 

      if (value == 0) 
      { 
       _streamBuffers = null; 
       _allocatedBytes = _position = _length = 0; 
       _pageCount = 0; 
       return; 
      } 

      int currentPageCount = GetPageCount(_allocatedBytes); 
      int neededPageCount = GetPageCount(value); 

      // Removes unused buffers if decreasing stream length 
      while (currentPageCount > neededPageCount) 
       _streamBuffers[--currentPageCount] = null; 

      AllocSpaceIfNeeded(value); 

      if (_position > (_length = value)) 
       _position = _length; 
     } 

     public override void Write(byte[] buffer, int offset, int count) 
     { 
      int currentPage = (int)(_position/PAGE_SIZE); 
      int currentOffset = (int)(_position % PAGE_SIZE); 
      int currentLength = PAGE_SIZE - currentOffset; 

      long startPosition = _position; 

      AllocSpaceIfNeeded(_position + count); 

      while (count != 0) 
      { 
       if (currentLength > count) 
        currentLength = count; 

       Array.Copy(buffer, offset, _streamBuffers[currentPage++], currentOffset, currentLength); 

       offset += currentLength; 
       _position += currentLength; 
       count -= currentLength; 

       currentOffset = 0; 
       currentLength = PAGE_SIZE; 
      } 
     } 

     #endregion Stream 
    } 
using ICSharpCode.SharpZipLib.GZip; 
using System; 
using System.IO; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

      // HugeMemoryStrem Test 

      string filename = @"gzip-filename.gz"; 

      HugeMemoryStream ms = new HugeMemoryStream(); 

      using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8, 16384, true)) 
      using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) 
      using (GZipInputStream gzipStream = new GZipInputStream(fs)) 
      using (StreamReader sr = new StreamReader(gzipStream, Encoding.UTF8, false, 16384, true)) 
      { 
       for (string line = sr.ReadLine(); line != null; line = sr.ReadLine()) 
        sw.WriteLine(line); 
      } 

      ms.Seek(0, SeekOrigin.Begin); 

      using (StreamReader srm = new StreamReader(ms, Encoding.UTF8, false, 16384, true)) 
      using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) 
      using (GZipInputStream gzipStream = new GZipInputStream(fs)) 
      using (StreamReader sr = new StreamReader(gzipStream, Encoding.UTF8, false, 16384, true)) 
      { 
       for (string line1 = sr.ReadLine(), line2 = srm.ReadLine(); line1 != null; line1 = sr.ReadLine(), line2 = srm.ReadLine()) 
       { 
        if (line1 != line2) 
         throw new InvalidDataException(); 
       } 
      } 
관련 문제