2010-05-11 3 views
9

Я использую Filestream для чтения большого файла (> 500 МБ), и я получаю OutOfMemoryException.OutOfMemoryException при чтении 500MB FileStream

Любые решения об этом.

Мой код:

using (var fs3 = new FileStream(filePath2, FileMode.Open, FileAccess.Read)) 
       { 
        byte[] b2 = ReadFully(fs3, 1024); 
       } 


public static byte[] ReadFully(Stream stream, int initialLength) 
    { 
     // If we've been passed an unhelpful initial length, just 
     // use 32K. 
     if (initialLength < 1) 
     { 
      initialLength = 32768; 
     } 

     byte[] buffer = new byte[initialLength]; 
     int read = 0; 

     int chunk; 
     while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) 
     { 
      read += chunk; 

      // If we've reached the end of our buffer, check to see if there's 
      // any more information 
      if (read == buffer.Length) 
      { 
       int nextByte = stream.ReadByte(); 

       // End of stream? If so, we're done 
       if (nextByte == -1) 
       { 
        return buffer; 
       } 

       // Nope. Resize the buffer, put in the byte we've just 
       // read, and continue 
       byte[] newBuffer = new byte[buffer.Length * 2]; 
       Array.Copy(buffer, newBuffer, buffer.Length); 
       newBuffer[read] = (byte)nextByte; 
       buffer = newBuffer; 
       read++; 
      } 
     } 
     // Buffer is now too big. Shrink it. 
     byte[] ret = new byte[read]; 
     Array.Copy(buffer, ret, read); 
     return ret; 
    } 

ответ

4

Вы удваивая размер буфера при каждом перераспределении, что означает, ранее выделенные блоки не могут быть использованы (они эффективно просачиваться). К тому времени, когда вы доберетесь до 500 МБ, вы пережевали 1 ГБ плюс накладные расходы. Фактически, это может быть 2 ГБ, поскольку, если вы нажмете 512 МБ, ваше следующее распределение составит 1 ГБ. В 32-битной системе это разрушает ваш процесс.

Поскольку это обычный файл, который вы читаете, просто запросите файловую систему для ее размера и предварительно распределите буфер за один раз.

+0

Пожалуйста, что это лучший код, я использую это: http://www.yoda.arachsys.com/csharp/readbinary.html Спасибо господин –

+1

+1: Да, выделяя размер буфера вам нужно это хорошая идея ... на самом деле, я удивлен, что .NET не имеет метода для чтения целого файла в массив байтов или какую-либо другую подобную структуру. – Powerlord

+2

Он делает. File.ReadAllBytes http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx Но это не то, что должен делать этот плакат. Чтение всех байтов 500-мегабайтного файла в память - это обычно плохая идея *, и в этом случае ... это очень плохая идея. Плакат явно имеет в виду первичную, но неизменную цель, которая не «считывает все байты файла в память». Он * думает * ему нужно прочитать все байты, но это неправда. – Cheeso

30

Код, который вы показываете, считывает все содержимое файла 500mb в смежный регион в памяти. Неудивительно, что вы получаете условие отсутствия памяти.

Решение: «Не делайте этого».

Что вы, действительно пытаясь сделать?


Если вы хотите полностью прочитать файл, это намного проще, чем метод ReadFully, который вы используете. Попробуйте следующее:

using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 
{ 
    byte[] buffer = new byte[fs.Length]; 
    int bytesRead = fs.Read(buffer, 0, buffer.Length); 
    // buffer now contains the entire contents of the file 
} 

Но ... используя этот код не решит вашу проблему. Он может работать для файла размером 500 МБ. Он не будет работать для файла 750mb или 1gb-файла. В какой-то момент вы достигнете лимита памяти в своей системе, и у вас будет такая же ошибка, что и с памятью, с которой вы начали.

Проблема в том, что вы пытаетесь одновременно сохранить все содержимое файла в памяти. Обычно это не нужно и обречено на провал, поскольку файлы растут по размеру. Это не проблема, когда размер файла 16k. В 500 мб это неправильный подход.

Вот почему я несколько раз спрашивал, что вы действительно пытаетесь сделать?


Похоже, вы хотите отправить содержимое файла в поток ответа ASPNET. Это вопрос. Не «как читать 500-мегабайтный файл в памяти?» Но «как отправить большой файл в поток ASPNET Response?»

Для этого еще раз, это довольно просто.

// emit the contents of a file into the ASPNET Response stream 
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) 
{ 
    Response.BufferOutput= false; // to prevent buffering 
    byte[] buffer = new byte[1024]; 
    int bytesRead = 0; 
    while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     Response.OutputStream.Write(buffer, 0, bytesRead); 
    } 
} 

Что она делает итеративно читать фрагмент из файла, и писать, что кусок в поток ответа, пока не будет больше ничего читать в файле. Это то, что подразумевается под «потоковым IO». Данные проходят через вашу логику, но никогда не удерживаются в одном месте, точно так же, как поток воды проходит через шлюз. В этом примере, не существует более 1k файлов данных в памяти в одно время (ну, не проводится код приложения, в любом случае. Есть другие IO буферов ниже в стеке.)

Это общий шаблон в потоковом IO. Изучите его, используйте.

Один трюк при перекачке данных в ответ ASPNET.OutputStream должен установить BufferOutput = false. По умолчанию ASPNET пытается буферировать свой вывод. В этом случае (файл 500 Мбайт) буферизация - плохая идея. Установка свойства BufferOutput в значение false приведет к тому, что ASPNET не попытается буферизовать все данные файла перед отправкой первого байта. Используйте это, когда знаете, что файл, который вы отправляете, очень велик. Данные будут по-прежнему отправляться в браузер правильно.

И даже это не полное решение. Вам нужно будет установить заголовки ответов и так далее. Наверное, вы об этом знаете.

+0

Только хотите прочитать большой файл в байте [] для отправки на странице asp.net. Функция ReadFully - это код yoda.arachsys.com. благодаря !!! http://www.yoda.arachsys.com/csharp/readbinary.html –

+1

Почему вы хотите, чтобы все содержимое этого большого файла в памяти было сразу? Что вы на самом деле пытаетесь сделать? – Cheeso

+0

Я хочу только прочитать большой файл в байте [], чтобы отправить его на страницу asp.net, например Response. Функция ReadFully - это код yoda.arachsys.com. благодаря !!! yoda.arachsys.com/csharp/readbinary.html –

Смежные вопросы