Question

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

Content-Disposition: attachment; filename=FILENAME

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

  

Restrictions grammaticales actuelles [RFC 2045]   valeurs de paramètre (et donc   Noms de fichiers Content-Disposition) à   US-ASCII. Nous reconnaissons le grand   opportunité de permettre l'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 du fait que la plupart des navigateurs Web actuels semblent autoriser les caractères non-US-ASCII, mais (en l’absence de norme) n’est pas d’accord sur le schéma de codage et la spécification du jeu de caractères du nom de fichier. La question est alors de savoir quels sont les divers schémas et codages utilisés par les navigateurs populaires si le nom du fichier & # 8220; na & # 239; vfile & # 8221; (sans guillemets et où la troisième lettre est U + 00EF) devait être encodé dans l'en-tête Content-Disposition?

Pour les besoins de cette question, les navigateurs populaires étant:

  • Firefox
  • Internet Explorer
  • Safari
  • Google Chrome
  • Opera

Autres conseils

Je sais que ceci est un ancien post, mais il reste très pertinent. J'ai constaté que les navigateurs modernes prennent en charge la norme rfc5987, qui permet le codage utf-8, codé en pourcentage (codé en url). Alors, Na! & # 239; ve fichier.txt devient:

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

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

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

IE8 et les versions antérieures ne le supportent pas non plus et vous devez utiliser le standard IE de codage utf-8, codé en pourcentage:

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

Dans 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 avec IE7, IE8, IE9, Chrome 13, Opera 11, FF5, Safari 5.

Mise à jour Novembre 2013:

Voici le code que j'utilise actuellement. Je dois toujours 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é et qu’ils ne peuvent pas analyser les noms de fichiers de manière fiable.

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 est maintenant testé dans IE7-11, Chrome 32, Opera 12, FF25 et Safari 6, en utilisant ce nom de fichier pour le téléchargement: & # 20320; & # 22909; abcABC & # 230; # 248; & # 229; & # 198; & # 216; & # 197; & # 228; & # 246; & # 252; & # 239; & # 235; & # 234; & # 238; & # 226; & # 233; & # 237; & # 225; & # 243; & # 250; & # 253; & # 241; & # 189; & # 167;! # & # 164;% & Amp; () = `@ < !> # 163; $ & # 8364; {[]} + & # 180; & # 168; ^ ~ '-_,;. 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ûrs pour Android. Notez que je ne sais pas quels caractères sont pris en charge sur Android, mais que j’ai bien vérifié qu’ils fonctionnaient à 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 fichier sur une ligne, conformément à la norme RFC6266, fonctionne sauf pour Android et IE7 + 8, et j'ai mis à jour le code en conséquence. Merci pour la suggestion.

@Thilo: Aucune idée de GoodReader ni d'aucun autre navigateur. Vous pourriez avoir de la chance en utilisant l'approche Android.

@Alex Zhukovskiy: Je ne sais pas pourquoi mais comme expliqué à Connect , il ne semble pas fonctionner très bien.

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 souhaitez, 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 exemple, mod_rewrite dans Apache), vous pouvez alors entièrement masquer la partie script.

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

/mot%C3%B6rhead   # motörhead

RFC 6266 décrit le & # 8220; Utilisation du champ d'en-tête Content-Disposition dans le Protocole de transfert hypertexte (HTTP) & # 8221 ;. Citant cela:

  

6. Considérations d'internationalisation

     

Le & # 8220; filename* & # 8221; paramètre ( section 4.3 ), en utilisant le codage défini   in [ RFC5987 ], permet au serveur de transmettre des caractères en dehors de la   ISO-8859-1, et spécifier éventuellement la langue   en cours d'utilisation.

Et dans leur section d'exemples :

  

Cet exemple est le même que celui ci-dessus, mais l'ajout du & "nom de 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
     

Remarque: les agents d'utilisateur qui ne prennent pas en charge le codage RFC 5987 .   ignorer & # 8220; filename & # 8221; quand cela se produit après & # 8220; multipart/form-data & # 8221;.

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

  • attwithisofnplain : nom de fichier en clair ISO-8859-1 avec guillemets et sans codage. Cela nécessite un nom de fichier entièrement ISO-8859-1 et ne contenant pas de signe de pourcentage, du moins pas devant des chiffres hexadécimaux.
  • attfnboth : deux paramètres dans l'ordre décrit ci-dessus. Cela devrait fonctionner pour la plupart des noms de fichiers sur la plupart des navigateurs, bien que IE8 utilise le & # 8220; <=> & # 8221; paramètre.

Ce RFC 5987 fait à son tour référence à RFC 2231 , qui décrit le format actuel. 2231 est principalement destiné au courrier et 5987 nous indique également quelles parties peuvent être utilisées pour les en-têtes HTTP. Ne confondez pas ceci avec les en-têtes MIME utilisés dans un <=> HTTP corps , régi par la RFC 2388 ( section 4.4 en particulier) et le brouillon HTML 5 .

Le document suivant est lié à le projet de RFC <> / a> mentionné par Jim dans sa réponse, répond plus à la question et mérite certainement une note directe ici:

Scénarios de test pour l'en-tête HTTP Content-Disposition et le codage RFC 2231/2047

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 du fichier à l'aide de

HttpUtility.UrlPathEncode(fileName)

Mettez le nom du 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_upon_download

J'ai testé plusieurs options. Les navigateurs ne supportent pas les spécifications et agissent différemment, je crois que les guillemets sont la meilleure option.

J'utilise les extraits de code suivants pour le codage (en supposant que nom_fichier contient le nom du 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 + "\"");

Dans l’API Web ASP.NET, je l’URL encoder le 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 non corrigé
IE 9 corrigé

J'ai testé le code suivant dans tous les principaux navigateurs, y compris les anciens Explorateurs (via le mode de 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.'"');

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

Je me suis retrouvé avec le code suivant dans mon " download.php " script (basé sur blogpost et ces scénarios 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) ));

Ceci utilise la méthode standard de nom de fichier = " ... " tant qu'il n'y a que iso-latin1 et & "safe &"; caractères utilisés; sinon, il ajoute le nom de fichier * = UTF-8 '' de manière encodée dans l'URL. Selon ce cas de test spécifique , il devrait fonctionner à partir de MSIE9 et, plus récemment, de FF, Chrome Safari; sur la version MSIE inférieure, il doit proposer un nom de fichier contenant la version ISO8859-1 du nom de fichier, avec des traits de soulignement sur les caractères ne figurant pas dans cet encodage.

Note finale: le max. La taille de chaque champ d’en-tête est de 8190 octets sur Apache. UTF-8 peut contenir jusqu'à quatre octets par caractère. après rawurlencode, c'est x3 = 12 octets par un caractère. Assez inefficace, mais il devrait encore être théoriquement possible d'avoir plus de 600 & «Sourires &»; % F0% 9F% 98% 81 dans le nom du fichier.

En PHP, cela s’est fait pour moi (en supposant que le nom du fichier soit au format 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 ISO-8859-1, vous pouvez envisager d’utiliser plutôt iconv.

Solution ASP classique

La plupart des navigateurs modernes prennent en charge le passage de Filename comme UTF-8 maintenant, mais comme ce fut le cas avec une solution de téléchargement de fichiers que j'utilise, basée sur FreeASPUpload.Net (le site n'existe plus, le lien pointe vers archive.org ) cela ne fonctionnerait pas car l'analyse du binaire reposait sur la lecture de chaînes codées ASCII sur un seul octet, ce qui fonctionnait bien lorsque vous passiez. Données encodées UTF-8 jusqu’à ce que vous obteniez des caractères non pris en charge par ASCII.

Cependant, j'ai pu trouver une solution pour obtenir le code permettant de lire et d'analyser le binaire au format 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

Le crédit va à Envoi de fichier ASP pur en implémentant la fonction BytesToString() à partir de include_aspuploader.asp dans mon propre code, j’ai pu faire fonctionner <=> les noms de fichiers.

Liens utiles

Juste une mise à jour depuis que j'essayais tout ça aujourd'hui en réponse à un problème client

  • À l'exception de Safari configuré pour le japonais, tous les navigateurs testés par nos clients fonctionnaient mieux avec filename = text.pdf - où text est une valeur client sérialisée par ASP.Net/IIS dans utf-8 sans codage d'URL. Pour une raison quelconque, Safari configuré pour l'anglais accepterait et enregistrerait correctement un fichier avec le nom japonais utf-8, mais ce même navigateur configuré pour le japonais enregistrerait le fichier avec les caractères utf-8 non interprétés. Tous les autres navigateurs testés semblaient fonctionner mieux / bien (quelle que soit la configuration de la langue) avec le nom de fichier utf-8 codé sans codage d’URL.
  • Je n'ai pas trouvé de navigateur implémentant Rfc5987 / 8187 du tout . J'ai testé avec les dernières versions de Chrome, Firefox, ainsi que IE 11 et Edge. J'ai essayé de définir l'en-tête avec juste le nom de fichier * = utf-8''texturlencoded.pdf, en le configurant avec les deux noms de fichier = text.pdf; nom de fichier * = utf-8''texturlencoded.pdf. Aucune caractéristique de Rfc5987 / 8187 ne semble avoir été traitée correctement dans aucune des solutions ci-dessus.

Nous avions un problème similaire dans une application Web. Nous avons fini par lire le nom du fichier à partir du code HTML <input type="file"> et à le définir sous la forme codée en URL dans un nouveau code HTML <input type="hidden">. Bien sûr, nous avons dû supprimer le chemin comme & "; C: \ fakepath \ &"; qui est retourné par certains navigateurs.

Bien sûr, cela ne répond pas directement à la question des PO, mais peut constituer une solution pour les autres.

Je code normalement avec URL (avec% xx) les noms de fichiers, et cela semble fonctionner dans tous les navigateurs. Vous voudrez peut-être quand même faire des tests.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top