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
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.
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.
-
il n'existe aucun moyen interopérable d'encoder les noms non ASCII dans
Content-Disposition
. la compatibilité du navigateur est un mess . -
le syntaxe théoriquement correcte pour L'utilisation de UTF-8 dans
Content-Disposition
est très étrange:filename*=UTF-8''foo%c3%a4
(Oui, c'est un astérisque, et pas de citations sauf une citation simple vide au milieu) -
This l'en-tête n'est pas tout à fait standard ( HTTP/1.1 spec reconnaît son existence , mais n'a pas besoin de clients pour le supporter).
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
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 .
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
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)
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 + "\"");
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;
}
}
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
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.'"');
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);
}
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.
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.
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
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.
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.
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.