2009-06-13 13 views
18

Necesito crear un ActionResult en una aplicación ASP.NET MVC que tenga un tipo de archivo .csv.Forma recomendada de crear un ActionResult con una extensión de archivo

Voy a proporcionar una lista de correo electrónico 'no llamar' a mis socios de marketing y quiero que tenga una extensión .csv en el tipo de archivo. Luego se abrirá automáticamente en Excel.

http://www.example.com/mailinglist/donotemaillist.csv?password=12334 

he hecho esto con éxito de la siguiente manera, pero yo quiero para asegurarse de que esta es la manera más favorable y recomendada de hacer esto.

[ActionName("DoNotEmailList.csv")] 
    public ContentResult DoNotEmailList(string username, string password) 
    { 
      return new ContentResult() 
      { 
       Content = Emails.Aggregate((a,b)=>a+Environment.NewLine + b), 
       ContentType = "text/csv" 
      }; 
    } 

Este Actionmethod responderá muy bien al enlace de arriba.

Me pregunto si hay alguna posibilidad de que surja un conflicto inesperado al tener una extensión de archivo como esta con cualquier versión diferente de IIS, cualquier tipo de filtro ISAPI o cualquier otra cosa en la que no pueda pensar ahora.

Necesito estar 100% seguro porque proporcionaré esto a socios externos y no quiero tener que cambiar de opinión más adelante. Realmente no puedo ver ningún problema, pero tal vez hay algo oscuro, u otra forma más de "MVC" de hacer esto.

Respuesta

19

Creo que su respuesta DEBE contener encabezado "Content-Disposition" en este caso. Crear personalizada ActionResult así:

public class MyCsvResult : ActionResult { 

    public string Content { 
     get; 
     set; 
    } 

    public Encoding ContentEncoding { 
     get; 
     set; 
    } 

    public string Name { 
     get; 
     set; 
    } 

    public override void ExecuteResult(ControllerContext context) { 
     if (context == null) { 
      throw new ArgumentNullException("context"); 
     } 

     HttpResponseBase response = context.HttpContext.Response; 

     response.ContentType = "text/csv"; 

     if (ContentEncoding != null) { 
      response.ContentEncoding = ContentEncoding; 
     } 

     var fileName = "file.csv"; 

     if(!String.IsNullOrEmpty(Name)) { 
      fileName = Name.Contains('.') ? Name : Name + ".csv"; 
     } 

     response.AddHeader("Content-Disposition", 
      String.Format("attachment; filename={0}", fileName)); 

     if (Content != null) { 
      response.Write(Content); 
     } 
    } 
} 

y utilizarlo en su lugar de acción ContentResult:

return new MyCsvResult { 
    Content = Emails.Aggregate((a,b) => a + Environment.NewLine + b) 
    /* Optional 
    * , ContentEncoding = "" 
    * , Name = "DoNotEmailList.csv" 
    */ 
}; 
+0

gracias! pero ¿puedes aclarar lo que quieres decir con DEBE? lo que tenía no parecen funcionar - aunque esta forma es mucho más preferible –

+0

Si sus declaraciones de acción ".csv" archivo a continuación, sus clientes quieren 'abrir' o 'salvar' - por lo que debe proporcionar 'Content-Disposition' de cabecera (aunque es opcional, vea http://www.ietf.org/rfc/rfc2183.txt). Sin este encabezado, el proceso de apertura de su archivo puede diferir en varios navegadores/sistemas operativos/máquinas –

+1

¿Hay algún error en el código? ¿No debería el nombre ser una cadena en lugar de una codificación? Además, el compilador me dice que ActionResult no tiene una propiedad de respuesta – ADB

6

Esta es la forma en que estoy haciendo algo similar. Lo estoy tratando como una descarga:

var disposition = String.Format(
    "attachment;filename=\"{0}.csv\"", this.Model.Name); 
Response.AddHeader("content-disposition", disposition); 

Esto debería aparecer en el navegador como una descarga de archivos con el nombre de archivo dado.

No puedo pensar en una razón por la cual la tuya no funcionaría, sin embargo.

28

que utiliza la acción FileContentResult también hacer algo similar.

public FileContentResult DoNotEmailList(string username, string password) 
{ 
     string csv = Emails.Aggregate((a,b)=>a+Environment.NewLine + b); 
     byte[] csvBytes = ASCIIEncoding.ASCII.GetBytes(csv); 
     return File(csvBytes, "text/csv", "DoNotEmailList.csv") 
} 

Agregará el encabezado content-disposition para usted.

+1

¡Tiene mucho más sentido que escribir una clase para hacer algo que ya está incorporado! ¡Aclamaciones! – Shawson

+0

Este es realmente el camino a seguir –

+0

Gracias por una respuesta limpia simple que es fácil de implementar en otra situación –

0

La respuesta que ha aceptado es suficiente, pero mantiene el contenido de la salida en la memoria a medida que la genera. ¿Qué sucede si el archivo que genera es bastante grande? Por ejemplo, cuando vuelcas un contenido de la tabla SQL. Su aplicación podría quedarse sin memoria. Lo que sí quieres en este caso es usar FileStreamResult. Una manera de alimentar a los datos en la corriente podría ser el uso de tubería, as I described here

Cuestiones relacionadas