2012-09-17 44 views
5

Tôi đang cố gắng trả về các tệp lớn thông qua bộ điều khiển ActionResult và đã triển khai lớp FileResult tùy chỉnh như sau.FileResult đệm vào bộ nhớ

public class StreamedFileResult : FileResult 
{ 
    private string _FilePath; 

    public StreamedFileResult(string filePath, string contentType) 
     : base(contentType) 
    { 
     _FilePath = filePath; 
    } 

    protected override void WriteFile(System.Web.HttpResponseBase response) 
    { 
     using (FileStream fs = new FileStream(_FilePath, FileMode.Open, FileAccess.Read)) 
     { 
      int bufferLength = 65536; 
      byte[] buffer = new byte[bufferLength]; 
      int bytesRead = 0; 

      while (true) 
      { 
       bytesRead = fs.Read(buffer, 0, bufferLength); 

       if (bytesRead == 0) 
       { 
        break; 
       } 

       response.OutputStream.Write(buffer, 0, bytesRead); 
      } 
     } 
    } 
} 

Tuy nhiên, vấn đề tôi gặp phải là toàn bộ tệp xuất hiện được lưu vào bộ nhớ. Tôi cần phải làm gì để ngăn chặn điều này?

+1

Tại sao bạn không sử dụng FileStreamResult hiện tại? –

+1

Ban đầu tôi đã thử sử dụng FileStreamResult nhưng nó cũng đệm tệp vào bộ nhớ. –

Trả lời

8

Bạn cần xóa phản hồi để tránh bị giật. Tuy nhiên nếu bạn tiếp tục lưu vào bộ đệm mà không đặt độ dài nội dung, người dùng sẽ không thấy bất kỳ tiến trình nào. Vì vậy, để người dùng có thể nhìn thấy tiến trình thích hợp, IIS đệm toàn bộ nội dung, tính toán độ dài nội dung, áp dụng nén và sau đó gửi phản hồi. Chúng tôi đã áp dụng quy trình sau để phân phối tệp cho khách hàng có hiệu suất cao.

FileInfo path = new FileInfo(filePath); 

// user will not see a progress if content-length is not specified 
response.AddHeader("Content-Length", path.Length.ToString()); 
response.Flush();// do not add anymore headers after this... 


byte[] buffer = new byte[ 4 * 1024 ]; // 4kb is a good for network chunk 

using(FileStream fs = path.OpenRead()){ 
    int count = 0; 
    while((count = fs.Read(buffer,0,buffer.Length)) >0){ 
     if(!response.IsClientConnected) 
     { 
      // network connection broke for some reason.. 
      break; 
     } 
     response.OutputStream.Write(buffer,0,count); 
     response.Flush(); // this will prevent buffering... 
    } 
} 

Bạn có thể thay đổi kích thước bộ đệm, nhưng 4kb lý tưởng là hệ thống tệp cấp thấp hơn cũng đọc bộ đệm theo khối 4kb.

+0

Cảm ơn bạn, điều đó hoạt động tuyệt vời! –

0

Akash Kava là một phần đúng và một phần sai. Bạn KHÔNG cần phải thêm tiêu đề Nội dung Độ dài hoặc thực hiện sau đó. Nhưng bạn DO, cần phải định kỳ tuôn ra response.OutputStream và sau đó response. ASP.NET MVC (ít nhất là phiên bản 5) sẽ tự động chuyển đổi điều này thành một phản hồi "Chuyển mã hóa: chunked".

byte[] buffer = new byte[ 4 * 1024 ]; // 4kb is a good for network chunk 

using(FileStream fs = path.OpenRead()){ 
    int count = 0; 
    while((count = fs.Read(buffer,0,buffer.Length)) >0){ 
     if(!response.IsClientConnected) 
     { 
      // network connection broke for some reason.. 
      break; 
     } 
     response.OutputStream.Write(buffer,0,count); 
     response.OutputStream.Flush(); 
     response.Flush(); // this will prevent buffering... 
    } 
} 

Tôi đã thử nghiệm và hoạt động.

+0

Nếu không có Content-Length, trình duyệt sẽ không hiển thị tiến trình vì nó không biết có bao nhiêu byte để tải xuống, mã hóa chunked chỉ cho khách hàng biết rằng vẫn còn nhiều nội dung hơn, nhưng không bao nhiêu. Vì vậy, nếu bạn có tệp lớn và trình duyệt chỉ tiếp tục nhận khối, nó sẽ không bao giờ hiển thị tiến trình%. –