Cómo configurar el nombre del archivo de descarga en la API web ASP.NET

140

En mi clase ApiController, tengo el siguiente método para descargar un archivo creado por el servidor.

public HttpResponseMessage Get(int id)
{
    try
    {
        string dir = HttpContext.Current.Server.MapPath("~"); //location of the template file
        Stream file = new MemoryStream();
        Stream result = _service.GetMyForm(id, dir, file);
        if (result == null)
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
        result.Position = 0;
        HttpResponseMessage response = new HttpResponseMessage();
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new StreamContent(result);
        return response;
    }
    catch (IOException)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError);
    }
}

Todo funciona perfectamente, excepto que el nombre del archivo de descarga predeterminado es su ID, por lo que el usuario puede tener que escribir su propio nombre de archivo cada vez que guarde el diálogo. ¿Hay alguna forma de establecer un nombre de archivo predeterminado en el código anterior?

Tae-Sung Shin
fuente
1
¿Puedes compartir el código que solías llamar a este método?
Yasser Shaikh
@Yasser: este es un método de controlador de API web; probablemente se llama a través de solicitudes HTTP que ingresan a IIS y las analiza y encuentra rutas y API web que llaman al método (y, por supuesto, también se llama mediante pruebas).
Dave Rael
¿Qué está pasando dentro de GetMyForm ()? ¿Convertir los archivos en una secuencia de bytes?
MSIslam
@MSIslam Tipo de. La función obtiene información del formulario del usuario y crea un archivo antes de convertirlo a la secuencia resultante.
Tae-Sung Shin

Respuestas:

289

Debe configurar el Content-Dispositionencabezado en HttpResponseMessage:

HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(result);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = "foo.txt"
};
Darin Dimitrov
fuente
21
Para cualquier persona curiosa sobre el tipo de disposición "adjunto", la lista completa de tipos de disposición se encuentra en iana.org/assignments/cont-disp/cont-disp.xhtml
sfuqua
1
Tienes otra respuesta para descargar un archivo aquí. ¿Importa si usas System.Net.Mime.ContentDispositiono ContentDispositionHeaderValue? ¿Es uno más actual y más preferido que el otro?
Luminoso
@Luminous una respuesta es para ActionResult, una paraHttpResponseMessage
weston
@weston su respuesta no responde a mi pregunta.
Luminoso
44
@Luminous "¿Importa" y "¿Es uno más actual y más preferido que el otro?" No y no. Uno es para MVC ActionResulty otro para WebApi HttpResponseMessage.
Weston
27

EDITAR: Como se mencionó en un comentario, Mi respuesta no tiene en cuenta los caracteres que deben escaparse como a ;. Debe usar la respuesta aceptada que Darin hizo si su nombre de archivo pudiera contener un punto y coma.

Agregue un Response.AddHeader para establecer el nombre del archivo

Response.AddHeader("Content-Disposition", "attachment; filename=*FILE_NAME*");

Simplemente cambie FILE_NAME al nombre del archivo.

KingPancake
fuente
2
Esto me resultó útil para resolver un problema similar al que hacía la pregunta. En mi caso, también me pareció útil cambiar el "archivo adjunto" a "en línea" para que IE8 me diera la opción de abrir siempre el tipo de archivo en cuestión.
Scott
2
No cubre escapar. ¿Qué pasa si el nombre del archivo incluye una ;u otra cosa con un significado especial?
Sam
Sam, cuando escribí esta respuesta hace 3 años, no me di cuenta de que necesitaba responder para escapar. Gracias por señalarme esto, hice una edición de mi respuesta explicando que mi respuesta no tiene en cuenta el escape. Pero mantener mi respuesta igual ya que parecía haber ayudado a la gente.
KingPancake
8

Si desea asegurarse de que el nombre del archivo esté codificado correctamente pero también evitar el mensaje HttpResponseMessage de WebApi, puede usar lo siguiente:

Response.AddHeader("Content-Disposition", new System.Net.Mime.ContentDisposition("attachment") { FileName = "foo.txt" }.ToString());

Puede usar ContentDisposition o ContentDispositionHeaderValue. Llamar a ToString en una instancia de cualquiera de ellos hará la codificación de los nombres de archivo por usted.

sorenhk
fuente
6

Creo que esto podría ser útil para usted.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Jarek
fuente
44
No cubre escapar. ¿Qué pasa si el nombre del archivo incluye una ;u otra cosa con un significado especial?
Sam
3

Debe agregar el encabezado de disposición de contenido a la respuesta:

 response.StatusCode = HttpStatusCode.OK;
 response.Content = new StreamContent(result);
 response.AppendHeader("content-disposition", "attachment; filename=" + fileName);
 return response;
Carl Raymond
fuente
3
No cubre escapar. ¿Qué pasa si el nombre del archivo incluye una ;u otra cosa con un significado especial?
Sam
3

Si está utilizando ASP.NET Core MVC, las respuestas anteriores están muy ligeramente alteradas ...

En mi método de acción (que devuelve async Task<JsonResult>) agrego la línea (en cualquier lugar antes de la declaración de devolución):

Response.Headers.Add("Content-Disposition", $"attachment; filename={myFileName}");
Peter
fuente
No cubre escapar. ¿Qué pasa si el nombre del archivo incluye un; o algo más con un significado especial?
KoalaBear
2

Esto debería hacer:

Response.AddHeader("Content-Disposition", "attachment;filename="+ YourFilename)
tucaz
fuente
2
No cubre escapar. ¿Qué pasa si el nombre del archivo incluye una ;u otra cosa con un significado especial?
Sam
0

Nota: La última línea es obligatoria.

Si no especificamos Access-Control-Expose-Headers , no obtendremos el nombre del archivo en la interfaz de usuario.

FileInfo file = new FileInfo(FILEPATH);

HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = file.Name
};
response.Content.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
Chandan YS
fuente
0

Teniendo en cuenta las respuestas anteriores, es necesario tener cuidado con los caracteres globalizados.

Supongamos que el nombre del archivo es: " Esdrújula prenda ñame - güena.jpg "

Resultado sin procesar para descargar: "Esdrújula prenda à ± ame - güena.jpg" [Feo]

Resultado del código HTML para descargar: "Esdr & _250; jula prenda & _241; ame - g & _252; ena.jpg" [Feo]

Resultado de UrlEncode para descargar: " Esdrújula + prenda + ñame + - + güena.jpg " [OK]

Entonces, casi siempre necesita usar UrlEncode sobre el nombre del archivo . Además, si configura el encabezado de disposición de contenido como cadena directa, debe asegurarse de rodear con comillas para evitar problemas de compatibilidad del navegador.

Response.AddHeader("Content-Disposition", $"attachment; filename=\"{HttpUtility.UrlEncode(YourFilename)}\"");

o con ayuda de clase:

var cd = new ContentDisposition("attachment") { FileName = HttpUtility.UrlEncode(resultFileName) };
Response.AddHeader("Content-Disposition", cd.ToString());

El System.Net.Mime. La clase ContentDisposition se encarga de las comillas.

Asereware
fuente