Tengo un problema al enviar archivos almacenados en una base de datos al usuario en ASP.NET MVC. Lo que quiero es una vista que enumere dos enlaces, uno para ver el archivo y dejar que el tipo MIME enviado al navegador determine cómo debe manejarse, y el otro para forzar una descarga.
Si elijo ver un archivo llamado SomeRandomFile.bak
y el navegador no tiene un programa asociado para abrir archivos de este tipo, entonces no tengo ningún problema con el valor predeterminado del comportamiento de descarga. Sin embargo, si elijo ver un archivo llamado SomeRandomFile.pdf
o SomeRandomFile.jpg
quiero que el archivo simplemente se abra. Pero también quiero mantener un enlace de descarga a un lado para poder forzar una solicitud de descarga independientemente del tipo de archivo. ¿Esto tiene sentido?
Lo he intentado FileStreamResult
y funciona para la mayoría de los archivos, su constructor no acepta un nombre de archivo de forma predeterminada, por lo que a los archivos desconocidos se les asigna un nombre de archivo basado en la URL (que no conoce la extensión para dar según el tipo de contenido). Si forzo el nombre del archivo al especificarlo, pierdo la capacidad del navegador de abrir el archivo directamente y aparece un mensaje de descarga. ¿Alguien más ha encontrado esto?
Estos son los ejemplos de lo que he probado hasta ahora.
//Gives me a download prompt.
return File(document.Data, document.ContentType, document.Name);
//Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);
//Gives me a download prompt (lose the ability to open by default if known type)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};
¿Alguna sugerencia?
ACTUALIZACIÓN:
Esta pregunta parece tocar un acorde con mucha gente, así que pensé en publicar una actualización. La advertencia sobre la respuesta aceptada a continuación que fue agregada por Oskar con respecto a los caracteres internacionales es completamente válida, y la he golpeado varias veces debido al uso de la ContentDisposition
clase. Desde entonces he actualizado mi implementación para solucionar esto. Si bien el código a continuación es de mi encarnación más reciente de este problema en una aplicación ASP.NET Core (Full Framework), también debería funcionar con cambios mínimos en una aplicación MVC anterior ya que estoy usando la System.Net.Http.Headers.ContentDispositionHeaderValue
clase.
using System.Net.Http.Headers;
public IActionResult Download()
{
Document document = ... //Obtain document from database context
//"attachment" means always prompt the user to download
//"inline" means let the browser try and handle it
var cd = new ContentDispositionHeaderValue("attachment")
{
FileNameStar = document.FileName
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
return File(document.Data, document.ContentType);
}
// an entity class for the document in my database
public class Document
{
public string FileName { get; set; }
public string ContentType { get; set; }
public byte[] Data { get; set; }
//Other properties left out for brevity
}
fuente
Inline = true
asegúrese de NO usar la sobrecarga de 3 parámetrosFile()
que toma el nombre de archivo como el tercer parámetro. Se va a trabajar en el IE, Chrome, pero reportará un encabezado duplicado y se niegan a presentar la imagen.Tuve problemas con la respuesta aceptada debido a la falta de sugerencias de tipo en la variable "documento":
var document = ...
así que estoy publicando lo que funcionó para mí como una alternativa en caso de que alguien más esté teniendo problemas.fuente
AppDomain.CurrentDomain.BaseDirectory
es queSystem.Web.HttpContext.Current.Server.MapPath("~")
esto puede funcionar mejor en un servidor real en comparación con una máquina local.La respuesta de Darin Dimitrov es correcta. Solo una adición:
Response.AppendHeader("Content-Disposition", cd.ToString());
puede provocar que el navegador no procese el archivo si su respuesta ya contiene un encabezado de "Disposición de contenido". En ese caso, es posible que desee utilizar:fuente
pdf
archivo, si configuro el tipo de contenido comoSystem.Net.Mime.MediaTypeNames.Application.Octet
, forzará la descarga incluso cuando configuroInline = true
, pero si configuro comoResponse.ContentType = MimeMapping.GetMimeMapping(filePath)
, es decirapplication/pdf
, se puede abrir correctamente en lugar de descargarResponse.Headers.Add
requieren el modo de canalización integrado de IIS. Además, incluso si el grupo de aplicaciones está configurado como integrado, arrojará una excepción. Solución. UsoResponse.AddHeader
. Vea el hilo SO: stackoverflow.com/questions/22313167/…Para ver el archivo (txt, por ejemplo):
Para descargar el archivo (txt, por ejemplo):
nota: para descargar el archivo debemos pasar el argumento fileDownloadName
fuente
Inline
propiedad en Content-Disposition me permite separar la capacidad de configurar el nombre del archivo del comportamiento de forzar la descarga o no.Creo que esta respuesta es más limpia (basada en https://stackoverflow.com/a/3007668/550975 )
fuente
application/octet-stream
y eso hizo que el archivo se descargara en lugar de mostrarse, y parece ser compatible.FileVirtualPath -> Research \ Global Office Review.pdf
fuente
El siguiente código funcionó para mí para obtener un archivo pdf de un servicio API y responderlo al navegador, espero que ayude;
fuente
Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider
El método de acción debe devolver FileResult con una secuencia, byte [] o ruta virtual del archivo. También necesitará saber el tipo de contenido del archivo que se está descargando. Aquí hay un ejemplo de método de utilidad (rápido / sucio). Enlace de video de muestra Cómo descargar archivos usando asp.net core
fuente
Si, como yo, ha llegado a este tema a través de los componentes de Razor mientras aprende Blazor, entonces encontrará que necesita pensar un poco más fuera de la caja para resolver este problema. Es un poco un campo de minas si (también como yo) Blazor es tu primera incursión en el mundo tipo MVC, ya que la documentación no es tan útil para tales tareas 'serviles'.
Entonces, al momento de escribir, no puede lograr esto usando Vanilla Blazor / Razor sin incrustar un controlador MVC para manejar la parte de descarga de archivos, un ejemplo del cual es el siguiente:
Luego, asegúrese de que el inicio de su aplicación (Startup.cs) esté configurado para usar MVC correctamente y que tenga la siguiente línea presente (agréguelo si no):
.. y finalmente modifique su componente para vincularlo al controlador, por ejemplo (ejemplo basado en iteración usando una clase personalizada):
¡Esperemos que esto ayude a cualquiera que haya luchado (como yo) a obtener una respuesta adecuada a esta pregunta aparentemente simple en los reinos de Blazor ...!
fuente