2010-05-11 15 views
9

Sto usando Filestream per leggere il file grande (> 500 MB) e ottengo l'OutOfMemoryException.OutOfMemoryException quando leggo 500MB FileStream

Qualsiasi soluzione a riguardo.

il mio codice è:

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; 
    } 

risposta

4

stai raddoppiando la dimensione del buffer ad ogni riallocazione, il che significa che i blocchi precedentemente assegnati non possono mai essere utilizzati (che effettivamente perdite). Nel momento in cui arrivi a 500 MB, hai masticato fino a 1 GB più i costi generali. In effetti, potrebbe essere 2 GB, dato che, se colpisci i 512 MB, la tua prossima allocazione sarà 1 GB. Su un sistema a 32 bit, questo distrugge il tuo processo.

Poiché si tratta di un file normale in cui si sta leggendo, basta interrogare il filesystem per le sue dimensioni e preallocare il buffer in una volta.

+0

prega, che è il codice migliore, ho utilizzare questo: http://www.yoda.arachsys.com/csharp/readbinary.html Grazie signore –

+1

+1: Sì, destinando la dimensione del buffer che serve è un buona idea ... in realtà, sono sorpreso che .NET non abbia un metodo per leggere un intero file in un array di byte o qualche altra struttura simile. – Powerlord

+2

Lo fa. File.ReadAllBytes http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx Ma questo non è ciò che questo poster dovrebbe fare. Leggere tutti i byte di un file da 500 MB in memoria è * di solito una cattiva idea *, e in questo caso ... è una pessima idea. Il poster ha chiaramente in mente un obiettivo primario, ma non dichiarato, che non "legge tutti i byte di un file in memoria". Lui * pensa * che ha bisogno di leggere tutti i byte, ma non è vero. – Cheeso

30

Il codice che si visualizza, legge tutto il contenuto del file 500mb in un'area contigua in memoria. Non sorprende che si verifichi una condizione di esaurimento della memoria.

La soluzione è "non farlo".

Cosa stai cercando di fare ?


Se si desidera leggere completamente un file, è molto più semplice del metodo ReadFully che si utilizza. Prova questo:

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 
} 

Ma ... l'utilizzo di questo codice non risolverà il problema. Potrebbe funzionare per un file da 500 MB. Non funzionerà con un file da 750 MB o con un file da 1 GB. Ad un certo punto raggiungerai il limite di memoria sul tuo sistema e avrai lo stesso errore di memoria esaurita con cui hai iniziato.

Il problema è che si sta tentando di tenere in memoria l'intero contenuto del file in una volta. Questo di solito non è necessario ed è destinato a fallire man mano che i file crescono di dimensioni. Non è un problema quando la dimensione del file è 16k. A 500mb, è l'approccio sbagliato.

Questo è il motivo per cui ho chiesto più volte, , cosa stai provando a fare?


Sembra che si desideri inviare il contenuto di un file a un flusso di risposta ASPNET. Questa è la domanda. Non "come leggere un file 500mb in memoria?" Ma "come inviare un file di grandi dimensioni al flusso di risposta ASPNET?"

Per questo, ancora una volta, è abbastanza semplice.

// 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); 
    } 
} 

ciò che fa è iterativo di leggere un pezzo dal file, e scrivere quel pezzo al flusso di risposta, fino a quando non c'è più niente da leggere nel file. Questo è ciò che si intende per "streaming IO". I dati passano attraverso la tua logica, ma non vengono mai tenuti tutti in un unico posto, proprio come un flusso d'acqua passa attraverso una chiusa. In questo esempio, non v'è più di 1k di dati del file in memoria in una sola volta (beh, non detenute dal il codice applicazione, in ogni caso. Ci sono altri buffer di IO inferiori nello stack.)

Questo è un modello comune nell'IO in streaming. Impara, usalo.

L'unico trucco per il pompaggio dei dati in Response.OutputStream di ASPNET è impostare BufferOutput = false. Per impostazione predefinita, ASPNET tenta di bufferizzare l'output. In questo caso (file 500mb), il buffering è una cattiva idea. L'impostazione della proprietà BufferOutput su false impedirà a ASPNET di tentare di bufferizzare tutti i dati del file prima di inviare il primo byte. Usalo quando sai che il file che stai inviando è molto grande. I dati verranno comunque inviati correttamente al browser.

E anche questa non è la soluzione completa. Dovrai impostare le intestazioni di risposta e così via. Immagino che tu ne sia consapevole, comunque.

+0

Solo leggere un grande file in byte [] per inviare una pagina asp.net. La funzione ReadFully è il codice di yoda.arachsys.com. Grazie !!! http://www.yoda.arachsys.com/csharp/readbinary.html –

+1

perché vuoi l'intero contenuto di questo file di grandi dimensioni in memoria in una sola volta? Cosa stai cercando di fare? – Cheeso

+0

Voglio solo leggere un grande file in byte [] per inviarlo alla pagina asp.net come Response. La funzione ReadFully è il codice di yoda.arachsys.com. Grazie !!! yoda.arachsys.com/csharp/readbinary.html –

Problemi correlati