2010-05-11 23 views
9

Tôi đang sử dụng Filestream để đọc tệp lớn (> 500 MB) và tôi nhận được OutOfMemoryException.OutOfMemoryException khi tôi đọc 500MB FileStream

Bất kỳ giải pháp nào về nó.

Mã của tôi là:

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

Trả lời

4

Bạn đang tăng gấp đôi kích thước bộ đệm của bạn tại mỗi phân bổ lại, có nghĩa là khối giao trước đây không bao giờ có thể được sử dụng (họ một cách hiệu quả rò rỉ). Đến thời điểm bạn nhận được 500 MB, bạn đã nhai thêm 1 GB cộng với chi phí. Trên thực tế, có thể là 2 GB, vì nếu bạn nhấn 512 MB, phân bổ tiếp theo của bạn sẽ là 1 GB. Trên hệ thống 32 bit, ngân hàng này phá vỡ quy trình của bạn.

Vì đó là tệp bình thường bạn đang đọc, chỉ cần truy vấn hệ thống tệp cho kích thước của nó và preallocate bộ đệm trong một lần.

+0

Xin vui lòng, đó là mã tốt nhất, tôi sử dụng: http://www.yoda.arachsys.com/csharp/readbinary.html Cảm ơn mister –

+1

+1: Có, phân bổ kích thước bộ đệm bạn cần là một ý tưởng hay ... thực sự, tôi ngạc nhiên rằng .NET không có phương thức để đọc toàn bộ tập tin vào một mảng byte hoặc một số cấu trúc tương tự khác. – Powerlord

+2

. File.ReadAllBytes http://msdn.microsoft.com/en-us/library/system.io.file.readallbytes.aspx Nhưng đó không phải là những gì poster này nên làm. Đọc tất cả các byte của một tập tin 500MB vào bộ nhớ là * thường là một ý tưởng tồi *, và trong trường hợp này, ... đó là một ý tưởng rất tồi. Các poster rõ ràng có trong tâm trí một mục tiêu chính, chưa unstated đó không phải là "đọc tất cả các byte của một tập tin vào bộ nhớ." Anh * nghĩ * anh ta cần đọc tất cả các byte, nhưng điều đó không đúng. – Cheeso

30

Mã bạn hiển thị, đọc tất cả nội dung của tệp 500MB vào vùng tiếp giáp trong bộ nhớ. Không có gì đáng ngạc nhiên khi bạn nhận được một điều kiện hết bộ nhớ.

Giải pháp là "không làm điều đó".

Bạn là gì thực sự đang cố gắng làm gì?


Nếu bạn muốn đọc tệp hoàn toàn, nó đơn giản hơn nhiều so với phương pháp ReadFully bạn sử dụng. Hãy thử điều này:

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 
} 

Nhưng ... bằng cách sử dụng mã này sẽ không giải quyết được sự cố của bạn. Nó có thể làm việc cho một tập tin 500MB. Nó sẽ không làm việc cho một tập tin 750mb, hoặc một tập tin 1gb. Tại một số điểm, bạn sẽ đạt đến giới hạn bộ nhớ trên hệ thống của bạn và bạn sẽ có cùng một lỗi bộ nhớ mà bạn đã bắt đầu.

Vấn đề là bạn đang cố giữ toàn bộ nội dung của tệp trong bộ nhớ cùng một lúc. Điều này thường không cần thiết và sẽ bị thất bại khi các tệp có kích thước lớn. Đó là không có vấn đề khi các filesize là 16k. Tại 500mb, đó là cách tiếp cận sai lầm.

Đây là lý do tại sao tôi đã hỏi nhiều lần, bạn đang thực sự cố gắng làm gì?


Có vẻ như bạn muốn gửi nội dung của tệp ra luồng phản hồi ASPNET. Đây là câu hỏi. Không phải "làm thế nào để đọc một tập tin 500MB vào bộ nhớ?" Nhưng "làm cách nào để gửi một tệp lớn tới luồng phản hồi ASPNET?"

Đối với điều này, một lần nữa, nó khá đơn giản.

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

Những gì nó đọc là đọc một đoạn từ tệp trả lời, và viết đoạn đó vào luồng Trả lời, cho đến khi không có gì để đọc trong tệp. Đây là những gì có nghĩa là "streaming IO". Dữ liệu đi qua logic của bạn, nhưng không bao giờ được giữ tất cả ở một nơi, giống như một dòng nước đi qua một cống. Trong ví dụ này, không bao giờ có nhiều hơn 1k dữ liệu tệp trong bộ nhớ cùng một lúc (tốt, không được giữ bởi mã ứng dụng của bạn, anyway. Có các bộ đệm IO khác thấp hơn trong ngăn xếp.)

Đây là một mô hình phổ biến trong luồng IO. Tìm hiểu nó, sử dụng nó.

Một mẹo khi bơm dữ liệu ra phản hồi của ASPNET.OutputStream là đặt BufferOutput = false. Theo mặc định, ASPNET cố gắng để đệm đầu ra của nó. Trong trường hợp này (tập tin 500MB), đệm là một ý tưởng tồi. Việc đặt thuộc tính BufferOutput thành false sẽ ngăn ASPNET cố gắng đệm tất cả dữ liệu tệp trước khi gửi byte đầu tiên. Sử dụng điều đó khi bạn biết tệp bạn đang gửi là rất lớn. Dữ liệu sẽ vẫn được gửi đến trình duyệt chính xác.

Và thậm chí đây không phải là giải pháp hoàn chỉnh. Bạn sẽ cần đặt tiêu đề phản hồi và v.v. Tôi đoán bạn biết điều đó, mặc dù.

+0

Chỉ muốn đọc một tệp lớn bằng byte [] để gửi trong trang asp.net. Chức năng ReadFully là mã của yoda.arachsys.com. cảm ơn !!! http://www.yoda.arachsys.com/csharp/readbinary.html –

+1

tại sao bạn muốn toàn bộ nội dung của tệp lớn này trong bộ nhớ cùng một lúc? Bạn đang thực sự * cố gắng làm gì? – Cheeso

+0

Tôi chỉ muốn đọc một tập tin lớn trong byte [] để gửi nó đến trang asp.net như Response. Chức năng ReadFully là mã của yoda.arachsys.com. cảm ơn !!! yoda.arachsys.com/csharp/readbinary.html –

Các vấn đề liên quan