Question Comment encoder le paramètre filename de l'en-tête Content-Disposition dans HTTP?


Les applications Web qui veulent forcer une ressource à être téléchargé plutôt que directement rendu dans un navigateur Web émettre un Content-Disposition en-tête dans la réponse HTTP du formulaire:

Content-Disposition: attachment; filename=NOM DE FICHIER

le filename paramètre peut être utilisé pour suggérer un nom pour le fichier dans lequel la ressource est téléchargée par le navigateur. RFC 2183 (Content-Disposition), cependant, déclare dans section 2.3 (Le paramètre de nom de fichier) que le nom de fichier ne peut utiliser que des caractères US-ASCII:

La grammaire actuelle [RFC 2045] limite   valeurs des paramètres (et donc   Noms de fichiers Content-Disposition) à   US-ASCII. Nous reconnaissons le grand   opportunité de permettre arbitraire   jeux de caractères dans les noms de fichiers, mais il est   au-delà de la portée de ce document à   définir les mécanismes nécessaires.

Il existe néanmoins des preuves empiriques, cependant, que les navigateurs Web les plus populaires semblent autoriser des caractères non-US-ASCII (en l'absence de norme) en désaccord sur le schéma de codage et la spécification du jeu de caractères. La question est alors, quels sont les différents schémas et encodages utilisés par les navigateurs populaires si le nom de fichier "naïvefile" (sans guillemets et où la troisième lettre est U + 00EF) doit être encodé dans l'en-tête Content-Disposition?

Aux fins de cette question, navigateurs populaires étant:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opéra

448
2017-09-18 15:25


origine


Réponses:


Il y a une discussion à ce sujet, y compris des liens vers les tests de navigateur et la rétrocompatibilité, dans le RFC 5987, "Codage des jeux de caractères et des langues pour les paramètres de champ d'en-tête HTTP (Hypertext Transfer Protocol)".

RFC 2183 indique que ces en-têtes doivent être encodés selon RFC 2184, qui était obsolète par RFC 2231, couvert par le projet de RFC ci-dessus.


81
2017-09-18 15:39



Je sais que c'est un vieux post mais c'est toujours très pertinent. J'ai trouvé que les navigateurs modernes supportent rfc5987, ce qui permet l'encodage utf-8, pourcentage encodé (encodé en url). Naïve file.txt devient alors:

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

Safari (5) ne le supporte pas. Au lieu de cela, vous devez utiliser le standard Safari pour écrire le nom du fichier directement dans votre en-tête codé en utf-8:

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

IE8 et les plus anciens ne le supportent pas non plus et vous devez utiliser la norme IE de l'encodage utf-8, pourcentage encodé:

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

En ASP.Net j'utilise le code suivant:

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);

J'ai testé ce qui précède en utilisant IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Mettre à jour Novembre 2013:

Voici le code que j'utilise actuellement. Je dois encore supporter IE8, donc je ne peux pas me débarrasser de la première partie. Il s'avère que les navigateurs sur Android utilisent le gestionnaire de téléchargement intégré d'Android et qu'il ne peut pas analyser de manière fiable les noms de fichiers de la manière standard.

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);

Ce qui précède maintenant testé dans IE7-11, Chrome 32, Opera 12, FF25, Safari 6, en utilisant ce nom de fichier pour le téléchargement: 你好 abcABCæøåÆØÅäöïïêêîâéíáóúýñ½§! # ¤% & () = `@ £ $ € {[]} +'¨ ^ ~ '-_,; .txt

Sur IE7, cela fonctionne pour certains personnages mais pas pour tous. Mais qui se soucie de IE7 de nos jours?

C'est la fonction que j'utilise pour générer des noms de fichiers sécurisés pour Android. Notez que je ne sais pas quels caractères sont supportés sur Android mais que j'ai testé que ceux-ci fonctionnent à coup sûr:

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: J'ai testé dans IE7 et IE8 et il s'est avéré que je n'avais pas besoin d'échapper à l'apostrophe ('). Avez-vous un exemple où cela échoue?

@Dave Van den Eynde: La combinaison des deux noms de fichiers sur une ligne selon RFC6266 fonctionne sauf pour Android et IE7 + 8 et j'ai mis à jour le code pour refléter cela. Merci pour la suggestion.

@Thilo: Aucune idée sur GoodReader ou tout autre non-navigateur. Vous pourriez avoir de la chance en utilisant l'approche Android.

@Alex Zhukovskiy: Je ne sais pas pourquoi mais comme discuté sur Relier cela ne semble pas fonctionner très bien.


304
2017-07-19 10:34



Il existe une alternative simple et très robuste: utilisez une URL contenant le nom de fichier souhaité.

Lorsque le nom après la dernière barre oblique est celui que vous voulez, vous n'avez pas besoin d'en-têtes supplémentaires!

Cette astuce fonctionne:

/real_script.php/fake_filename.doc

Et si votre serveur prend en charge la réécriture d’URL (par ex. mod_rewrite dans Apache), vous pouvez alors masquer entièrement la partie script.

Les caractères dans les URL doivent être en UTF-8, encodés octet par octet:

/mot%C3%B6rhead   # motörhead

147
2017-10-19 18:26



RFC 6266 Décrit le "Utilisation du champ d'en-tête Content-Disposition dans le protocole HTTP (Hypertext Transfer Protocol)". Citant de cela:

6. Considérations d'internationalisation

Le "filename*"Paramètre (Section 4.3), en utilisant l'encodage défini   dans [RFC5987], permet au serveur de transmettre des caractères en dehors du   Jeu de caractères ISO-8859-1, et éventuellement spécifier la langue   utilisé.

Et dans leur section d'exemples:

Cet exemple est le même que celui ci-dessus, mais en ajoutant le "nom de fichier"   paramètre de compatibilité avec les agents utilisateurs ne mettant pas en œuvre    RFC 5987:

Content-Disposition: attachment;
                     filename="EURO rates";
                     filename*=utf-8''%e2%82%ac%20rates

Remarque: Les agents utilisateurs qui ne prennent pas en charge le RFC 5987 codage   ignorer "filename*"Quand cela se produit après"filename".

Dans Annexe D Il existe également une longue liste de suggestions pour accroître l'interopérabilité. Il indique également un site qui compare les implémentations. Les tests passe-tout actuels adaptés aux noms de fichiers courants incluent:

  • attwithisofnplain: nom de fichier ISO-8859-1 simple avec guillemets et sans codage. Cela nécessite un nom de fichier qui est tout ISO-8859-1 et ne contient pas de signes de pourcentage, du moins pas devant les chiffres hexadécimaux.
  • attfnboth: deux paramètres dans l'ordre décrit ci-dessus. Devrait fonctionner pour la plupart des noms de fichiers sur la plupart des navigateurs, même si IE8 utilisera le "filename"Paramètre.

Cette RFC 5987 à son tour références RFC 2231, qui décrit le format réel. 2231 est principalement pour le courrier, et 5987 nous dit quelles parties peuvent être utilisées pour les en-têtes HTTP. Ne pas confondre avec les en-têtes MIME utilisés dans un multipart/form-data HTTP corps, qui est régi par RFC 2388 (section 4.4 en particulier) et HTML 5 brouillon.


55
2018-01-05 12:48



Le document suivant lié à le projet de RFC mentionné par Jim dans sa réponse plus loin adresse la question et vaut vraiment une note directe ici:

Cas de test pour l'en-tête HTTP Content-Disposition et l'encodage RFC 2231/2047


16
2017-09-18 16:08



dans asp.net mvc2 j'utilise quelque chose comme ceci:

return File(
    tempFile
    , "application/octet-stream"
    , HttpUtility.UrlPathEncode(fileName)
    );

Je suppose que si vous n'utilisez pas mvc (2) vous pouvez simplement encoder le nom de fichier en utilisant

HttpUtility.UrlPathEncode(fileName)

11
2017-07-15 15:08



J'utilise les extraits de code suivants pour l'encodage (en supposant nom de fichier contient le nom de fichier et l'extension du fichier, c'est-à-dire: test.txt):


PHP:

if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
     header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $fileName ) . '"' );
}
else
{
     header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $fileName ) );
}

Java:

fileName = request.getHeader ( "user-agent" ).contains ( "MSIE" ) ? URLEncoder.encode ( fileName, "utf-8") : MimeUtility.encodeWord ( fileName );
response.setHeader ( "Content-disposition", "attachment; filename=\"" + fileName + "\"");

8
2018-04-19 11:29



Dans l'API Web ASP.NET, je code l'URL du nom de fichier:

public static class HttpRequestMessageExtensions
{
    public static HttpResponseMessage CreateFileResponse(this HttpRequestMessage request, byte[] data, string filename, string mediaType)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new MemoryStream(data);
        stream.Position = 0;

        response.Content = new StreamContent(stream);

        response.Content.Headers.ContentType = 
            new MediaTypeHeaderValue(mediaType);

        // URL-Encode filename
        // Fixes behavior in IE, that filenames with non US-ASCII characters
        // stay correct (not "_utf-8_.......=_=").
        var encodedFilename = HttpUtility.UrlEncode(filename, Encoding.UTF8);

        response.Content.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("attachment") { FileName = encodedFilename };
        return response;
    }
}

IE 9 Not fixed
IE 9 Fixed


8
2018-06-25 08:10



Mettez votre nom de fichier entre guillemets. Résolu le problème pour moi. Comme ça:

Content-Disposition: attachment; filename="My Report.doc"

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download


8
2017-07-10 15:01



J'ai testé le code suivant dans tous les principaux navigateurs, y compris les anciens Explorers (via le mode de compatibilité), et cela fonctionne bien partout:

$filename = $_GET['file']; //this string from $_GET is already decoded
if (strstr($_SERVER['HTTP_USER_AGENT'],"MSIE"))
  $filename = rawurlencode($filename);
header('Content-Disposition: attachment; filename="'.$filename.'"');

5
2018-05-31 15:48



Si vous utilisez un backend nodejs, vous pouvez utiliser le code suivant que j'ai trouvé ici

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves "!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987, 
            // so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);
}

5
2017-09-25 12:45