2012-09-17 19 views
5

Estoy tratando de devolver archivos grandes a través de un controlador ActionResult y he implementado una clase FileResult personalizada como la siguiente.FileResult almacenado en la 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); 
      } 
     } 
    } 
} 

Sin embargo, el problema que tengo es que todo el archivo parece estar almacenado en la memoria. ¿Qué debería hacer para evitar esto?

+1

¿Por qué no utiliza FileStreamResult existente? –

+1

Inicialmente intenté usar FileStreamResult pero también almacena el archivo en la memoria. –

Respuesta

8

Necesita enjuagar la respuesta para evitar el almacenamiento en búfer. Sin embargo, si mantiene el almacenamiento en búfer sin establecer la longitud del contenido, el usuario no verá ningún progreso. Entonces, para que los usuarios vean el progreso adecuado, IIS almacena todo el contenido, calcula la longitud del contenido, aplica compresión y luego envía la respuesta. Hemos adoptado el siguiente procedimiento para entregar archivos al cliente con alto rendimiento.

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

Puede cambiar el tamaño de búfer, pero 4kb es ideal como sistema de archivos de nivel inferior también lee búfer en trozos de 4 kb.

+0

Gracias señor, ¡eso funciona genial! –

0

Akash Kava es en parte correcto y en parte incorrecto. NO es necesario que agregue el encabezado Content-Length o realice el color después. Pero usted lo HACE, necesita enjuagar periódicamente response.OutputStream y luego response. ASP.NET MVC (al menos la versión 5) convertirá automáticamente esto en una respuesta "Transferencia de codificación: fragmentada".

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

Lo probé y funciona.

+0

Sin Content-Length, el navegador no mostrará el progreso porque no sabe cuántos bytes hay para descargar, la codificación fragmentada solo le dice al cliente que todavía hay más contenido, pero no cuánto. Entonces, si tiene un archivo enorme y el navegador sigue recibiendo fragmentos, nunca mostrará el progreso%. –

Cuestiones relacionadas