2012-09-17 11 views
5

Próbuję zwrócić duże pliki za pośrednictwem kontrolera ActionResult i zaimplementowałem niestandardową klasę FileResult, jak poniżej.FileResult buforowany do pamięci

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

Problemem jest to, że cały plik wydaje się być buforowany do pamięci. Co powinienem zrobić, aby temu zapobiec?

+1

Dlaczego nie używasz istniejącego FileStreamResult? –

+1

Początkowo próbowałem użyć FileStreamResult, ale również buforuje plik do pamięci. –

Odpowiedz

8

Musisz przepłukać odpowiedź, aby zapobiec buforowaniu. Jednak jeśli będziesz kontynuować buforowanie bez ustawiania długości treści, użytkownik nie będzie widział żadnych postępów. Aby użytkownicy mogli zobaczyć prawidłowy postęp, usługi IIS buforują całą zawartość, obliczają długość treści, stosują kompresję, a następnie wysyłają odpowiedź. Przyjęliśmy następującą procedurę, aby dostarczyć pliki do klienta o wysokiej wydajności.

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

Można zmienić rozmiar bufora, ale 4KB jest idealnym systemem plików niższy poziom także odczytuje bufor w kawałki 4kB.

+0

Dziękuję, proszę pana, to działa świetnie! –

0

Akash Kava ma częściowo rację, a częściowo jest nie tak. NIE musisz dodawać nagłówka Content-Length lub spłukiwać później. Ale TY, musisz okresowo przepłukiwać response.OutputStream, a następnie response. ASP.NET MVC (przynajmniej wersja 5) automatycznie przekonwertuje to na odpowiedź "Transfer-Encoding: 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... 
    } 
} 

Przetestowałem to i działa.

+0

Bez funkcji Content-Length przeglądarka nie będzie wyświetlać postępu, ponieważ nie wie, ile bajtów jest do pobrania, kodowanie kodowane mówi klientowi, że jest jeszcze więcej treści, ale nie ile. Więc jeśli masz ogromny plik, a przeglądarka wciąż otrzymuje porcje, nigdy nie pokazuje postępu%. –

Powiązane problemy