2010-05-11 83 views
9

我正在使用Filestream讀大文件(> 500 MB),我得到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 MB時,你已經咀嚼了1 GB的額外開銷。事實上,它可能是2 GB,因爲如果你達到512 MB,你的下一個分配將是1 GB。在32位系統上,這破壞了你的過程。

由於這是一個正常的文件,你正在閱讀,只是查詢文件系統的大小和一次性預先分配緩衝區。

+0

請,這是最好的代碼,我用這個:http://www.yoda.arachsys.com/csharp/readbinary.html 感謝老總 – 2010-05-11 13:04:29

+1

+1:是的,分配你需要的緩衝區大小是好主意......實際上,我很驚訝.NET沒有將整個文件讀入字節數組或其他類似結構的方法。 – Powerlord 2010-05-12 14:30:54

+2

它的確如此。 File.ReadAllBytes http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx但這不是這張海報應該做的。將500MB文件的所有字節讀入內存通常是個壞主意,在這種情況下,這是一個非常糟糕的主意。這個海報顯然有一個主要的但沒有說明的目標,它不是「將文件的所有字節讀入內存」。他*認爲*他需要讀取所有字節,但事實並非如此。 – Cheeso 2010-05-20 11:50:37

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 
} 

但是...:試試這個。它可能適用於500MB文件。它不適用於750MB文件或1GB文件。在某些時候,您將達到系統內存的極限,並且您將遇到與之前相同的內存不足錯誤。

問題是您試圖一次將文件的全部內容保存在內存中。這通常是不必要的,並且隨着文件尺寸的增大註定要失敗。文件大小爲16k時沒有問題。 500MB,這是錯誤的方法。

這就是爲什麼我問了幾次,你真的想要做什麼


聽起來像要將文件的內容發送到ASPNET響應流。這是個問題。不是「如何將500MB文件讀入內存?」但是「如何發送大文件到ASPNET響應流?」

爲此,它再次相當簡單。

// 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的Response.OutputStream的一個竅門是設置BufferOutput = false。默認情況下,ASPNET會嘗試緩衝其輸出。在這種情況下(500MB文件),緩衝是一個壞主意。將BufferOutput屬性設置爲false將防止ASPNET在發送第一個字節之前嘗試緩衝所有文件數據。當你知道你發送的文件非常大時使用它。數據仍然會正確發送到瀏覽器。

即使這不是完整的解決方案。您需要設置響應標題等。不過,我想你知道這一點。

+0

只想在byte []中讀取一個大文件發送到一個asp.net頁面。 ReadFully函數是yoda.arachsys.com的代碼。謝謝 !!! http://www.yoda.arachsys.com/csharp/readbinary.html – 2010-05-11 13:04:07

+1

你爲什麼要一次將這個大文件的全部內容存儲在內存中?你真的*試圖做什麼? – Cheeso 2010-05-11 14:03:05

+0

我只想在byte []中讀取一個大文件,將它發送到類似Response的asp.net頁面。 ReadFully函數是yoda.arachsys.com的代碼。謝謝 !!! yoda.arachsys.com/csharp/readbinary.html – 2010-05-12 06:29:22