2012-09-17 13 views
5

Sto cercando di restituire file di grandi dimensioni tramite un controller ActionResult e ho implementato una classe FileResult personalizzata come la seguente.FileResult bufferizzato in memoria

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

Tuttavia il problema che sto avendo è che l'intero file sembra essere memorizzato nella memoria. Cosa dovrei fare per evitarlo?

+1

Perché non si utilizza il FileStreamResult esistente? –

+1

Inizialmente ho provato a utilizzare FileStreamResult, ma memorizza anche il file in memoria. –

risposta

8

È necessario svuotare la risposta per evitare il buffering. Tuttavia, se continui a eseguire il buffering senza impostare la lunghezza del contenuto, l'utente non vedrà alcun progresso. Quindi, per consentire agli utenti di vedere il progresso corretto, IIS memorizza l'intero contenuto, calcola la lunghezza del contenuto, applica la compressione e quindi invia la risposta. Abbiamo adottato la seguente procedura per consegnare file al client con prestazioni elevate.

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

È possibile modificare la dimensione del buffer, ma 4KB è ideale come file system di livello inferiore si legge anche di buffer in blocchi di 4 KB.

+0

Grazie signore, funziona benissimo! –

0

Akash Kava è parzialmente giusto e in parte sbagliato. NON è necessario aggiungere l'intestazione Content-Length o eseguire lo svuotamento in seguito. Ma tu FARE, devi periodicamente svuotare response.OutputStream e poi response. ASP.NET MVC (almeno la versione 5) convertirà automaticamente questo in una risposta "Trasferisci-Codifica: 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... 
    } 
} 

L'ho provato e funziona.

+0

Senza Content-Length, il browser non mostrerà il progresso perché non sa quanti byte ci sono da scaricare, la codifica chunked dice al client che c'è ancora più contenuto, ma non quanto. Quindi se hai un file enorme e il browser continua a ricevere pezzi, non mostrerà mai il progresso%. –

Problemi correlati