¿Cómo codificar el parámetro de nombre de archivo del encabezado Content-Disposition en HTTP?

Resuelto Atif Aziz asked hace 16 años • 22 respuestas

Las aplicaciones web que desean forzar la descarga de un recurso en lugar de representarlo directamente en un navegador web emiten un Content-Dispositionencabezado en la respuesta HTTP del formulario:

Content-Disposition: attachment; filename=FILENAME

El filenameparámetro se puede utilizar para sugerir un nombre para el archivo en el que el navegador descarga el recurso. RFC 2183 (Disposición de contenido), sin embargo, establece en la sección 2.3 (El parámetro de nombre de archivo) que el nombre del archivo solo puede usar caracteres US-ASCII:

La gramática actual [RFC 2045] restringe los valores de los parámetros (y, por lo tanto, los nombres de archivos de disposición de contenido) a US-ASCII. Reconocemos la gran conveniencia de permitir conjuntos de caracteres arbitrarios en los nombres de archivos, pero definir los mecanismos necesarios está más allá del alcance de este documento.

Sin embargo, existe evidencia empírica de que los navegadores web más populares hoy en día parecen permitir caracteres que no son ASCII de EE. UU., pero (por falta de un estándar) no están de acuerdo con el esquema de codificación y la especificación del juego de caracteres del nombre del archivo. La pregunta es entonces, ¿cuáles son los diversos esquemas y codificaciones empleados por los navegadores populares si el nombre del archivo “naïvefile” (sin comillas y donde la tercera letra es U+00EF) necesita codificarse en el encabezado Content-Disposition?

A los efectos de esta pregunta, los navegadores populares son:

  • Google Chrome
  • Safari
  • Internet Explorer o borde
  • Firefox
  • Ópera
Atif Aziz avatar Sep 18 '08 22:09 Atif Aziz
Aceptado

Sé que esta es una publicación antigua pero sigue siendo muy relevante. Descubrí que los navegadores modernos admiten rfc5987, que permite la codificación utf-8, con codificación porcentual (codificada en URL). Entonces Naïve file.txt se convierte en:

Content-Disposition: attachment; filename*=UTF-8''Na%C3%AFve%20file.txt

Safari (5) no admite esto. En su lugar, deberías utilizar el estándar de Safari para escribir el nombre del archivo directamente en tu encabezado codificado en utf-8:

Content-Disposition: attachment; filename=Naïve file.txt

IE8 y versiones anteriores tampoco lo admiten y es necesario utilizar el estándar IE de codificación utf-8, codificado en porcentaje:

Content-Disposition: attachment; filename=Na%C3%AFve%20file.txt

En ASP.Net utilizo el siguiente código:

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.Browser.Browser == "Safari")
    contentDisposition = "attachment; filename=" + fileName;
else
    contentDisposition = "attachment; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

Probé lo anterior usando IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Actualización de noviembre de 2013:

Aquí está el código que uso actualmente. Todavía tengo que soportar IE8, así que no puedo deshacerme de la primera parte. Resulta que los navegadores de Android utilizan el administrador de descargas integrado de Android y no pueden analizar de manera confiable los nombres de archivos de la manera estándar.

string contentDisposition;
if (Request.Browser.Browser == "IE" && (Request.Browser.Version == "7.0" || Request.Browser.Version == "8.0"))
    contentDisposition = "attachment; filename=" + Uri.EscapeDataString(fileName);
else if (Request.UserAgent != null && Request.UserAgent.ToLowerInvariant().Contains("android")) // android built-in download manager (all browsers on android)
    contentDisposition = "attachment; filename=\"" + MakeAndroidSafeFileName(fileName) + "\"";
else
    contentDisposition = "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + Uri.EscapeDataString(fileName);
Response.AddHeader("Content-Disposition", contentDisposition);

Lo anterior ahora probado en IE7-11, Chrome 32, Opera 12, FF25, Safari 6, usando este nombre de archivo para descargar: 你好abcABCæøåÆØÅäöüïëêîâéíáóúýñ½§!#¤%&()=`@£$€{[]}+´¨ ^~'-_,;.txt

En IE7 funciona para algunos personajes pero no para todos. ¿Pero a quién le importa IE7 hoy en día?

Esta es la función que uso para generar nombres de archivos seguros para Android. Tenga en cuenta que no sé qué caracteres son compatibles con Android, pero he probado que funcionan con seguridad:

private static readonly Dictionary<char, char> AndroidAllowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~'=()[]{}0123456789".ToDictionary(c => c);
private string MakeAndroidSafeFileName(string fileName)
{
    char[] newFileName = fileName.ToCharArray();
    for (int i = 0; i < newFileName.Length; i++)
    {
        if (!AndroidAllowedChars.ContainsKey(newFileName[i]))
            newFileName[i] = '_';
    }
    return new string(newFileName);
}

@TomZ: Probé en IE7 e IE8 y resultó que no necesitaba escapar del apóstrofe ('). ¿Tiene un ejemplo en el que falla?

@Dave Van den Eynde: Combinar los dos nombres de archivos en una línea según RFC6266 funciona excepto para Android e IE7+8 y he actualizado el código para reflejar esto. Gracias por la sugerencia.

@Thilo: No tengo idea sobre GoodReader o cualquier otro que no sea navegador. Es posible que tengas algo de suerte utilizando el enfoque de Android.

@Alex Zhukovskiy: No sé por qué, pero como se comentó en Connect , no parece funcionar muy bien.

Martin Ørding-Thomsen avatar Jul 19 '2011 10:07 Martin Ørding-Thomsen
  • No existe una forma interoperable de codificar nombres que no sean ASCII en formato Content-Disposition. La compatibilidad del navegador es un desastre .

  • La sintaxis teóricamente correcta para el uso de UTF-8 Content-Dispositiones muy extraña: filename*=UTF-8''foo%c3%a4(sí, es un asterisco y no hay comillas, excepto una comilla simple vacía en el medio)

  • Este encabezado no es del todo estándar ( la especificación HTTP/1.1 reconoce su existencia , pero no requiere que los clientes lo admitan).

Existe una alternativa simple y muy sólida: use una URL que contenga el nombre del archivo que desea .

Cuando el nombre después de la última barra es el que desea, ¡no necesita ningún encabezado adicional!

Este truco funciona:

/real_script.php/fake_filename.doc

Y si su servidor admite la reescritura de URL (por ejemplo, mod_rewriteen Apache), puede ocultar completamente la parte del script.

Los caracteres de las URL deben estar en UTF-8, codificados en URL byte a byte:

/mot%C3%B6rhead   # motörhead
Kornel avatar Oct 19 '2008 18:10 Kornel

Hay una discusión sobre esto, incluidos enlaces a pruebas de navegador y compatibilidad con versiones anteriores, en el RFC 5987 propuesto , "Conjunto de caracteres y codificación de idioma para parámetros de campo de encabezado del Protocolo de transferencia de hipertexto (HTTP).

RFC 2183 indica que dichos encabezados deben codificarse de acuerdo con RFC 2184 , que quedó obsoleto por RFC 2231 , cubierto por el borrador de RFC anterior.

Jim avatar Sep 18 '2008 15:09 Jim