2012-10-03 29 views
5

Tengo una aplicación de navegador de archivos en MVC4 que le permite descargar un archivo seleccionado de un controlador.ASP.NET MVC: devolver grandes cantidades de datos de FileResult

Actualmente, FileResult devuelve el flujo del archivo, junto con los otros encabezados de respuesta. Si bien esto funciona bien para archivos más pequeños, los archivos que son más grandes generan una OutOfMemoryException.

Lo que me gustaría hacer es transmitir el archivo desde el controlador, sin almacenar en memoria intermedia de forma similar a HttpReponse.TransmitFile en WebForms.

¿Cómo se puede lograr esto?

Respuesta

2

sí se puede mediante el uso de Web API con el fin de transmitir los archivos, echar un vistazo a este artículo Dealing with large files in ASP.NET Web API

+0

Gracias - cuando se utiliza el enlace, después de añadir el 'GlobalConfiguration.Configuration.Services.Replace (typeof (WebHostBufferPolicySelector), nuevo NoBufferPolicySelector());' línea de mi archivo Global.asax.cs, consigo el siguiente error: 'El tipo de servicio WebHostBufferPolicySelector no es compatible. ¿Alguna ayuda allí? –

+0

La pregunta es sobre MVC, no API web –

0

Adicionalmente a la relación anterior que describe la configuración, es posible utilizar TransmitFile a los archivos "corriente". Sin embargo, TransmitFile tiene algunos retrocesos en algunos clientes http.

Este es mi código para "transmitir" archivos al cliente, como una alternativa para TransmitFile. Tenga en cuenta que he tiene una constante de configuración para deshacer la escritura de archivo (que tiene el mismo problema que el suyo OutOfMemory): Código

private void RenderContent(string contentType,string fileName, bool inline); 

método:

FileInfo fi = new FileInfo(fileName); 

Response.ClearHeaders(); 
Response.ClearContent(); 

Response.ContentType = contentType; 
Response.CacheControl = "No-cache"; 
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); 
Response.AddHeader("Content-Length", fi.Length.ToString()); 
Response.AddHeader("Content-Disposition", (inline ? "inline" : "attachment") + "; filename=\"" + fi.Name + "\""); 

if (cAppInfos.Constant["FallBackToWriteFile"] != null && cAppInfos.Constant["FallBackToWriteFile"] == "true") 
{ 
    Response.WriteFile(fileName); 
} 
else 
{ 
    int chunkSize = 8192; 
    byte[] buffer = new byte[chunkSize]; 
    int offset = 0; 
    int read = 0; 
    using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) 
    { 
     while ((read = fs.Read(buffer, offset, chunkSize)) > 0) 
     { 
      if (!Response.IsClientConnected) 
       break; 

      Response.OutputStream.Write(buffer, 0, read); 
      Response.Flush(); 
     } 
    } 
} 

He utilizado este código para evitar algunos problemas en transmisión de archivos pdf al antiguo plugin de acrobat reader. Para el parámetro en línea, puede usar "falso".

Además, puede utilizar try/catch alrededor de este código, ya que cualquiera de Response.IsClientConnecter/Write/Flush puede generar excepciones si el cliente se desconecta.

Sin embargo, no estoy usando MVC, y no estoy seguro de que esto sea aceptable para este techno, ya que si no ha usado TransmitFile, puede tener el mismo problema con este código. Si está tratando de incrustar archivos en el elemento de páginas web (¿tal vez como base64?) Esta no es la solución.

Deseo obtener más información acerca de sus requisitos, si necesita otras maneras de lograr sus objetivos.

+0

Además, la opción de presentar el resultado como "archivos continuos" puede no ser relevante en el controlador y puede moverse en la presentación, donde obtiene acceso al objeto Response. El controlador puede solicitarle a la capa de presentación que transmita la ... filestream/filename? – Echtelion

+0

En respuesta a su "Controlador puede simplemente pedirle a la capa de presentación que transmita el ..." comentario, ya que tendría que servir la transmisión de la misma manera, ¿esto solo presentaría el mismo problema en una ubicación diferente? –

+0

No soy un experto MVC, pero le sugiero que mueva el problema a un lugar donde pueda resolverlo (es decir, un lugar con acceso al objeto Response) – Echtelion

8

Puede desactivar el búfer de respuesta antes de devolver el resultado del archivo.

Response.BufferOutput = false; 
return File(fileStream, contentType); 
+1

Esta es la única solución (limpia) que funcionó para mí , ¡Gracias! –

+1

Tenga en cuenta que para archivos realmente grandes (2GB +) también debe establecer 'HttpCompletionOption.ResponseHeadersRead' para evitar' No se pueden escribir más bytes en el búfer que la excepción del tamaño de búfer máximo configurado: 2147483647'. –

Cuestiones relacionadas