Les applications Web qui veulent forcer une ressource à être téléchargée plutôt que directement dans un navigateur Web, émettez 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. RFC 218 (Content-Disposition) indique toutefois dans section 2. (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 les valeurs de paramètre (et donc les noms de fichier Content-Disposition) à US-ASCII. Nous reconnaissons qu'il est hautement souhaitable de permettre des jeux de caractères arbitraires dans les noms de fichiers, mais il est au-delà de la portée de ce document de définir les mécanismes nécessaires.
Il existe néanmoins des preuves empiriques que la plupart des navigateurs Web actuels semblent autoriser les caractères non-US-ASCII, mais (en l'absence de norme) en désaccord 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 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 encodé dans l'en-tête Content-Disposition.
Pour les besoins de cette question, les navigateurs populaires étant:
Il existe une discussion à ce sujet, y compris des liens vers les tests de navigateur et la compatibilité ascendante, dans la proposition RFC 5987 , "Codage du jeu de caractères et du langage pour les paramètres de champ d'en-tête HTTP (Hypertext Transfer Protocol)". "
RFC 218 indique que ces en-têtes doivent être codés conformément à RFC 2184 , qui est obsolète par RFC 2231 , couvert par le projet de RFC ci-dessus.
Je sais que ceci est un ancien post, mais il est toujours 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). 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 la norme Safari d’écriture du nom de fichier directement dans votre en-tête encodé utf-8:
Content-Disposition: attachment; filename=Naïve file.txt
IE8 et les versions antérieures ne le prennent pas en charge 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é Android et ne peuvent pas analyser de manière fiable les noms de fichiers de 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);
Les éléments ci-dessus sont maintenant testés dans IE7-11, Chrome 32, Opera 12, FF25, Safari 6, en utilisant ce nom de fichier pour le téléchargement: `@ £ $ € {[]} + ´¨ ^ ~ '-_,;. 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 vérifié 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 fichier sur une ligne, conformément au RFC6266, fonctionne à l'exception de Android et de 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 un peu de chance en utilisant l'approche Android.
@ Alex Zhukovskiy: Je ne sais pas pourquoi mais, comme indiqué dans le chapitre Connexion , cela ne semble pas très bien fonctionner.
Il n'y a pas de moyen interopérable de coder des noms non-ASCII dans Content-Disposition
. La compatibilité du navigateur est un gâchis .
Le syntaxe théoriquement correcte pour l'utilisation de UTF-8 dans Content-Disposition
est très bizarre: filename*=UTF-8''foo%c3%a4
(oui, c'est un astérisque et pas de guillemets sauf un guillemet simple vide au milieu )
Cet en-tête est un peu standard ( HTTP/1.1 spec reconnaît son existence , mais ne nécessite pas que les clients le prennent en charge).
Il existe une alternative simple et très robuste: utilisez une URL contenant le nom du 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 masquer entièrement la partie script.
Les caractères dans les URL doivent être en UTF-8, codés url par octet:
/mot%C3%B6rhead # motörhead
RFC 6266 décrit le “ utilisation du champ d'en-tête Content-Disposition dans le protocole de transfert d'hypertexte (HTTP) ”. Citant cela:
6. Considérations d'internationalisation
Le paramètre "
filename*
" ( chapitre 4. ), utilisant le codage défini dans [ RFC5987 ], permet au serveur de transmettre des caractères en dehors de la norme ISO-8859-1. jeu de caractères, ainsi que de spécifier éventuellement la langue utilisée.
Et dans leur section exemples :
Cet exemple est identique à l'exemple ci-dessus, mais ajoute le paramètre "nom de fichier" pour assurer la compatibilité avec les agents d'utilisateur 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 codage RFC 5987 ignorent "
filename*
" lorsqu'il survient après "filename
".
Dans Annexe D , une longue liste de suggestions visant à accroître l’interopérabilité est également proposée. Il pointe également sur n site qui compare les implémentations . Les tests actuels "passe-tout" adaptés aux noms de fichiers courants incluent:
filename
”.Ce RFC 5987 référence à son tour RFC 2231 , qui décrit le format réel. 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 corps multipart/form-data
HTTP , régi 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 répond plus en détail à la question et mérite certainement une note directe ici:
Cas 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 en utilisant
HttpUtility.UrlPathEncode(fileName)
Mettez le nom du 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
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 l'encodage (en supposant que NomFichier 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, j’URL code 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;
}
}
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);
}
J'ai testé le code suivant dans tous les principaux navigateurs, y compris les anciens Explorateurs (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.'"');
Je me suis retrouvé avec le code suivant dans mon script "download.php" (basé sur this 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) ));
Ceci utilise la méthode standard filename = "..." tant qu'il n'y a que des caractères iso-latin1 et "safe" 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, ainsi que sur les versions récentes de FF, Chrome, Safari; sur la version inférieure de MSIE, il devrait offrir 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.
Dans PHP, cela s’est fait pour moi (en supposant que le nom du fichier est codé en 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 du fichier * = utf-8, il utilisera la version UTF8 du nom du fichier, sinon il utilisera le nom du 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 voudrez peut-être envisager d'utiliser iconv
à la place.
Le framework PHP Symfony 4 a $filenameFallback
dans HeaderUtils::makeDisposition
. Vous pouvez regarder dans cette fonction pour plus de détails - c'est semblable aux réponses ci-dessus.
Exemple d'utilisation:
$filenameFallback = preg_replace('#^.*\.#', md5($filename) . '.', $filename);
$disposition = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $filename, $filenameFallback);
$response->headers->set('Content-Disposition', $disposition);
Juste une mise à jour puisque j'essayais tout ça aujourd'hui en réponse à un problème client
La plupart des navigateurs modernes prennent en charge le passage de la Filename
comme UTF-8
maintenant, mais comme ce fut le cas avec une solution de téléchargement de fichier que j'utilise, qui était basée sur FreeASPUpload.Net (le site n'existe plus, le lien pointe vers archive.org ) il ne fonctionnerait pas car l'analyse du binaire reposant sur la lecture d'un octet unique ASCII chaînes codées, qui fonctionnaient bien lorsque vous transmettiez des données codées UTF-8 jusqu'à ce que vous obteniez les caractères ASCII ne sont pas pris en charge.
Cependant, j'ai pu trouver une solution pour obtenir le code à lire et analyser le binaire en 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 à Pure ASP Upload de fichier en implémentant la fonction BytesToString()
de include_aspuploader.asp
dans mon propre code, j'ai pu obtenir UTF-8
noms de fichiers. travail.