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

les applications Web que voulez forcer une ressource qui doit être téléchargé plutôt que de rendu dans un navigateur Web, la publication d'une Content-Disposition en-tête de la réponse HTTP de la forme:

Content-Disposition: attachment; filename=FILENAME

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

[RFC 2045] grammaire limite valeurs des paramètres (et donc Contenu-Disposition noms des fichiers) vers US-ASCII. Nous reconnaissons le grand l'opportunité de permettre l'arbitraire les 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 y a des preuves empiriques, néanmoins, que la plupart des navigateurs Web populaires aujourd'hui semblent permettre non-US-ASCII caractères encore (pour l'absence d'une norme) en désaccord sur le schéma d'encodage et la spécification de jeu de caractères du nom de fichier. La Question est alors de savoir quels sont les différents schémas et codages utilisés par les navigateurs populaires si le nom de fichier "naïvefile" (sans guillemets et où la troisième lettre est U+00EF) devait être codé dans l'en-tête Content-Disposition?

aux fins de cette question, navigateurs populaires étant:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opéra
457
demandé sur bignose 2008-09-18 19:25:07

17 réponses

il y a une discussion à ce sujet, y compris les liens vers les tests de navigateur et la compatibilité ascendante, dans la proposition RFC 5987 , " jeu de caractères et encodage de la langue pour les paramètres de champ D'en-tête du protocole de transfert hypertexte (HTTP)."

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 RFC ci-dessus.

81
répondu Jim 2013-09-25 07:10:59

je sais que c'est un vieux poste mais il est encore très pertinent. J'ai trouvé que les navigateurs modernes prennent en charge rfc5987, qui permet l'encodage utf-8, pourcentage encodé (url-encoded). Puis Naïf de fichier.txt devient:

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

Safari (5) ne supporte pas cela. Vous devriez plutôt utiliser la norme Safari d'écrire le nom du fichier directement dans votre en-tête encodé utf-8:

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

IE8 et 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

In 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é le ci-dessus en utilisant IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Mise À Jour Novembre 2013:

Voici le code que j'utilise actuellement. Je dois encore soutenir IE8, donc je ne peux pas me débarrasser de la première partie. Il s'avère que les navigateurs sur Android utilisez le gestionnaire de téléchargement Android intégré et 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: #¤%&()=`@£$€{[]}+^~'-_,;.txt

Sur IE7 ça marche pour certains personnages, mais pas tous. Mais qui se soucie de IE7 de nos jours?

C'est la fonction que j'utilise pour générer un fichier sécurisé des noms pour Android. Notez que je ne sais pas quels caractères sont pris en charge 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ù il échoue?

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

@Thilo: aucune idée de GoodReader ou de tout autre non-navigateur. Vous pourriez avoir un peu de chance en utilisant L'approche Androïde.

@Alex Zhukovskiy: Je ne sais pas pourquoi mais comme discuté sur Connect il ne semble pas fonctionner terriblement bien.

314
répondu Martin Ørding-Thomsen 2016-09-13 19:59:07

il existe une alternative simple et très robuste: utilisez une URL qui contient le nom de fichier que vous voulez .

quand le nom après le dernier slash est celui que vous voulez, vous n'avez pas besoin de headers supplémentaires!

cette astuce fonctionne:

/real_script.php/fake_filename.doc

et si votre serveur prend en charge la réécriture D'URL (par exemple mod_rewrite dans Apache), alors vous pouvez entièrement masquer la partie script.

les caractères des URLs doivent être UTF-8, urlencoded byte-by-byte:

/mot%C3%B6rhead   # motörhead
150
répondu Kornel 2015-01-06 15:42:50

RFC 6266 décrit Utilisation du champ D'en-tête Content-Disposition dans le protocole de transfert hypertexte (HTTP) ".

6. Considérations Relatives À L'Internationalisation

le " filename* " paramètre ( Section 4.3 ), en utilisant le codage défini dans [ RFC5987 ], permet le serveur pour transmettre des caractères en dehors de la ISO-8859-1 jeu de caractères, et aussi de spécifier la langue en cours d'utilisation.

et dans leur exemples section :

cet exemple est le même que celui ci-dessus, mais en ajoutant le " nom du fichier" paramètre de compatibilité avec les agents utilisateurs non implémentés RFC 5987 :

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

Note: Les agents utilisateurs qui ne supportent pas l'encodage RFC 5987 ignorer " filename* "quand il se produit après" filename ".

Dans Annexe D il y a aussi une longue liste de propositions visant à accroître l'interopérabilité. Il indique également un site qui compare les implémentations .

  • attwithisofnplain : simple ISO-8859-1 nom de fichier avec guillemets doubles et sans encodage. Cela nécessite un nom de fichier qui est tout ISO-8859-1 et ne contient pas de signes de pourcentage, du moins pas en avant des 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, bien que IE8 utilisera le paramètre " filename ".

That RFC 5987 à son tour fait référence à 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 ainsi. Ne confondez pas cela avec les en-têtes MIME utilisés à l'intérieur D'un multipart/form-data HTTP body , qui est régie par RFC 2388 ( section 4.4 en particulier) et le HTML 5 draft .

58
répondu MvG 2014-01-05 12:54:48

le document suivant lié à le projet de RFC mentionné par Jim dans sa réponse aborde la question et vaut certainement une note directe ici:

Cas de Test pour HTTP-tête Content-Disposition et RFC 2231/2047 Encodage

16
répondu Atif Aziz 2017-05-23 12:10:28

in 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 du fichier en utilisant

HttpUtility.UrlPathEncode(fileName)
11
répondu Elmer 2011-12-12 20:03:02

j'utilise les extraits de code suivants pour encoder (en supposant que fileName contient le nom de fichier et l'extension du fichier, i.e.: 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
répondu Vassilen Dontchev 2015-01-06 15:45:59

In ASP.NET Web API, I url encodent le nom du 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
répondu martinoss 2015-06-25 08:10:39

mettez votre nom de fichier entre guillemets. Résolu le problème pour moi. Comme ceci:

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

http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download

8
répondu Dmitry Kaigorodov 2015-07-10 15:01:51

j'ai testé le code suivant dans tous les principaux navigateurs, y compris les explorateurs plus âgés (via le mode compatibilité), et il 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
répondu Stano 2012-07-21 21:08:26

si vous utilisez un backend nodejs vous pouvez utiliser le code suivant 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
répondu Emanuele Spatola 2016-05-27 08:36:28

j'ai fini avec le code suivant dans mon" download.php " script (basé sur ce blogpost et ces cas de test ).

$il1_filename = utf8_decode($filename);
$to_underscore = "\"\#*;:|<>/?";
$safe_filename = strtr($il1_filename, $to_underscore, str_repeat("_", strlen($to_underscore)));

header("Content-Disposition: attachment; filename=\"$safe_filename\""
.( $safe_filename === $filename ? "" : "; filename*=UTF-8''".rawurlencode($filename) ));

utilise la méthode standard du nom de fichier="..."aussi longtemps qu'il n'y a que des caractères iso-latin1 et "sûrs" utilisés; sinon, il ajoute le nom du fichier*=UTF-8" de manière url-encodée. Selon ce cas d'essai spécifique , il devrait fonctionner à partir de MSIE9 jusqu'à, et sur les récents FF, Chrome, Safari; sur la version inférieure de MSIE, il devrait offrir le nom de fichier contenant la version ISO8859-1 du nom de fichier, avec des soulignements sur les caractères non dans ce codage.

note finale: Le max. la taille de chaque champ d'en-tête est 8190 octets sur apache. UTF-8 peut être jusqu'à quatre octets par caractère; après rawurlencode, il est x3 = 12 octets par caractère. Plutôt inefficace, mais il devrait quand même être théoriquement possible d'avoir plus de 600 "sourires" %F0%9F%98% 81 dans le nom de fichier.

4
répondu renergy 2015-04-05 15:45:29

en PHP cela a fait pour moi (en supposant que le nom de fichier est encodé UTF8):

header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));

testé contre IE8-11, Firefox et Chrome.

Si le navigateur peut interpréter nom de fichier* = utf-8 il utilisera la version UTF8 du nom de fichier, sinon il utilisera le nom de fichier décodé. Si votre nom de fichier contient des caractères qui ne peuvent pas être représentés dans la norme ISO-8859-1, vous pouvez utiliser iconv à la place.

3
répondu Gustav 2016-05-28 10:58:10

Classic ASP Solution

la plupart des navigateurs modernes prennent en charge passer le Filename comme UTF-8 maintenant, mais comme c'était le cas avec une solution de téléchargement de fichier que j'utilise qui a été basé sur FreeASPUpload.Net (le site n'existe plus, le lien pointe vers archive.org ) it wouldn't work as the parsing of the binary relied on reading single byte ASCII encoded strings, which worked fine when you passed UTF-8 données encodées jusqu'à ce que vous obtenez des caractères ASCII ne supporte pas.

cependant j'ai pu trouver une solution pour obtenir le code pour lire et analyser le binaire comme UTF-8.

Public Function BytesToString(bytes)    'UTF-8..
  Dim bslen
  Dim i, k , N 
  Dim b , count 
  Dim str

  bslen = LenB(bytes)
  str=""

  i = 0
  Do While i < bslen
    b = AscB(MidB(bytes,i+1,1))

    If (b And &HFC) = &HFC Then
      count = 6
      N = b And &H1
    ElseIf (b And &HF8) = &HF8 Then
      count = 5
      N = b And &H3
    ElseIf (b And &HF0) = &HF0 Then
      count = 4
      N = b And &H7
    ElseIf (b And &HE0) = &HE0 Then
      count = 3
      N = b And &HF
    ElseIf (b And &HC0) = &HC0 Then
      count = 2
      N = b And &H1F
    Else
      count = 1
      str = str & Chr(b)
    End If

    If i + count - 1 > bslen Then
      str = str&"?"
      Exit Do
    End If

    If count>1 then
      For k = 1 To count - 1
        b = AscB(MidB(bytes,i+k+1,1))
        N = N * &H40 + (b And &H3F)
      Next
      str = str & ChrW(N)
    End If
    i = i + count
  Loop

  BytesToString = str
End Function

crédit va à pur téléchargement de fichier ASP en mettant en œuvre la fonction BytesToString() de include_aspuploader.asp dans mon propre code, j'ai été en mesure d'obtenir UTF-8 noms de fichiers de travail.


Liens Utiles

1
répondu Lankymart 2017-05-23 12:26:23

nous avons eu un problème similaire dans une application web, et avons fini par lire le nom du fichier du HTML <input type="file"> , et le réglage que dans la forme url-encoded dans un nouveau HTML <input type="hidden"> . Bien sûr, nous avons dû enlever le chemin comme "C:\fakepath\" qui est retourné par certains navigateurs.

bien sûr, cela ne répond pas directement à la question OPs, mais peut être une solution pour d'autres.

-1
répondu Andrei I 2015-01-27 11:54:13

normalement J'URL-encode (avec %xx) les noms de fichiers, et il semble fonctionner dans tous les navigateurs. Vous pourriez faire quelques essais de toute façon.

-2
répondu Dario Solera 2008-09-18 15:28:29

j'ai trouvé la solution, qui fonctionne pour tous mes navigateurs (ie. tous les navigateurs que j'ai installés - IE8, FF16, Opera 12, Chrome 22).

ma solution est décrite dans un autre fil: "1519100920 servlet Java fichier de téléchargement des caractères spéciaux

ma solution est basée sur le fait, comment les navigateurs essayant de lire la valeur du paramètre filename . S'il n'y a pas de jeu de caractères spécifié dans le paramètre filename (par exemple filename*=utf-8''test.xml ) les navigateurs s'attendent à ce que la valeur soit encodée dans le codage natif du navigateur.

différents navigateurs s'attendent à un encodage natif différent. Habituellement, l'encodage natif du navigateur est utf-8 (FireFox, Opera, Chrome). Mais L'encodage natif D'IE est Win-1250. (Je ne sais rien sur les autres navigateurs.)

donc, si nous mettons de la valeur dans le paramètre filename , qui est encodé par utf-8/win-1250 selon le navigateur de l'utilisateur, il devrait fonctionner. Au moins, ça fonctionne pour moi.

en bref, si nous avons un fichier nommé omáčka.xml ,

pour FireFox, Opera et Chrome I répondre à cet en-tête (encodé en utf-8):

Content-Disposition: attachment; filename="omáčka.xml"

et pour IE j'ai la réponse à cette en-tête (encodé dans win-1250):

Content-Disposition: attachment; filename="omáèka.jpg"

exemple Java est dans mon post qui est mentionné ci-dessus.

-3
répondu sporak 2017-05-23 11:55:06