2009-09-01 8 views
42

J'ai un problème avec les en-têtes HTTP, ils sont encodés en ASCII et je veux fournir une vue pour télécharger des fichiers dont les noms peuvent être non ASCII.Comment encoder le nom de fichier UTF8 pour les en-têtes HTTP? (Python, Django)

response['Content-Disposition'] = 'attachment; filename="%s"' % (vo.filename.encode("ASCII","replace"),) 

Je ne veux pas utiliser des fichiers statiques qui servent à même problème avec les noms de fichiers non ASCII, mais dans ce cas il y aurait un problème avec le système de fichiers et il est l'encodage du nom de fichier. (Je ne connais pas l'os cible.)

J'ai déjà essayé urllib.quote(), mais cela soulève l'exception KeyError.

Peut-être que je fais quelque chose de mal mais c'est peut-être impossible.

+1

Je me rends compte que j'ai des années de retard, mais ... l'exception KeyError me dérange vraiment. Je ne veux pas dire simplement "de temps en temps je rencontre ce problème", je veux dire, j'ai soumis un correctif à Python pour corriger cela il ya quelques années, argumenté pendant un certain temps, puis décidé qu'ils ne voulaient pas changer Python 2. Je J'ai résolu ce problème dans Python 3, mais ils n'ont jamais accepté mon correctif dans Python 2. La solution consiste à utiliser .encode ('utf-8') en premier, puis à utiliser urllib.quote. Mais c'est pour l'encodage d'URL qui n'est pas la manière standard de les mettre dans les en-têtes. – mgiuca

Répondre

34

Ceci est une FAQ.

Il n'y a pas de moyen interopérable de le faire. Certains navigateurs implémentent des extensions propriétaires (IE, Chrome), d'autres implémentent RFC 2231 (Firefox, Opera).

Voir les cas de test au http://greenbytes.de/tech/tc2231/.

Mise à jour: depuis novembre 2012, tous les navigateurs de bureau actuels prennent en charge l'encodage défini dans RFC 6266 et RFC 5987 (Safari> = 6, IE> = 9, Chrome, Firefox, Opera, Konqueror). Ne pas envoyer un nom de fichier dans Content-Disposition.

+0

Merci! Les choses les plus faciles sont les plus difficiles à trouver;) –

+0

Plus récemment, Julian a dressé un profil de la RFC2231 à cet effet: http://datatracker.ietf.org/doc/draft-reschke-rfc2231-in-http/ –

+4

comme http://greenbytes.de/tech/webdav/rfc5987.html –

30

Il n'y a aucun moyen de faire fonctionner les paramètres d'en-tête non-ASCII cross-browser (*). Au lieu de cela, envoyez simplement "Content-Disposition: attachment" et laissez le nom du fichier sous la forme d'une chaîne UTF-8 encodée en URL dans la partie "PATH_INFO" de votre URL, pour que le navigateur la récupère et l'utilise par défaut . Les URL UTF-8 sont traitées de manière beaucoup plus fiable par les navigateurs que tout ce qui a trait à Content-Disposition.

(*: En fait, il n'y a même pas une norme actuelle qui dit comment il devrait être fait que les relations entre les RFCs 2616, 2231 et 2047 sont à peu dysfonctionnelle, quelque chose que Julian tente de s'éclaircira spec . niveau de soutien du navigateur cohérent est dans un futur lointain)

+3

La première réponse contient d'excellentes informations, mais vous avez réellement résolu le problème. Merci! –

+0

Grande réponse ... – cherouvim

+7

Depuis que cette réponse est sortie, une RFC sur ce sujet a été publiée. On notera la construction 'filename * =' que seuls les navigateurs les plus récents prennent en charge et qui vous permettra d'utiliser UTF-8, codé comme dans la RFC 5987. http://tools.ietf.org/html/rfc6266#appendix-D –

0

Un hack.

if (Request.UserAgent.Contains("IE")) 
{ 
    // IE will accept URL encoding, but spaces don't need to be, and since they're so common.. 
    filename = filename.Replace("%", "%25").Replace(";", "%3B").Replace("#", "%23").Replace("&", "%26"); 
} 
+2

En général, les sniffs de sniffeurs d'utilisateurs sont utilisés par [ces serveurs buggés] (http://greenbytes.de/tech/tc2231/#buggy-senders) et sont responsables de beaucoup de cas de test tc2231/rfc6266. – Tobu

26

Notez que, en 2011, RFC 6266 (en particulier l'annexe D) ont pesé sur cette question et a des recommandations précises à suivre.

À savoir, vous pouvez émettre un filename avec uniquement des caractères ASCII, suivi de filename* avec un nom de fichier au format RFC 5987 pour les agents qui le comprennent.

Typiquement, cela ressemblera filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf, où le nom de fichier Unicode (« Mon Résumé.pdf ») est codée en UTF-8, puis codés pour cent (note, ne PAS utiliser + pour les espaces).

Veuillez lire les RFC 6266 et RFC 5987 (ou utiliser une bibliothèque robuste et testée qui résume ceci pour vous), car mon résumé ici manque de détails importants.

+0

C'est ce dont j'avais besoin pour un noeud final de téléchargement de fichier dans mon projet Django. Je vous remercie! – macguru2000

2

Je peux dire que j'ai eu du succès en utilisant le nouveau format (RFC 5987) de spécification d'un en-tête codé avec le formulaire de courrier électronique (RFC 2231). J'ai trouvé la solution suivante qui est basée sur le code du projet django-sendfile.

import unicodedata 
from django.utils.http import urlquote 

def rfc5987_content_disposition(file_name): 
    ascii_name = unicodedata.normalize('NFKD', file_name).encode('ascii','ignore').decode() 
    header = 'attachment; filename="{}"'.format(ascii_name) 
    if ascii_name != file_name: 
     quoted_name = urlquote(file_name) 
     header += '; filename*=UTF-8\'\'{}'.format(quoted_name) 

    return header 

# e.g. 
    # request['Content-Disposition'] = rfc5987_content_disposition(file_name) 

Je n'ai testé mon code sur Python 3.4 avec Django 1.8. Donc, le solution in django-sendfile similaire peut vous mieux.

Il y a un long standing ticket dans le tracker de Django qui le reconnaît mais aucun correctif n'a encore été proposé. Donc, malheureusement, cela est aussi proche de l'utilisation d'une bibliothèque testée robuste que je pourrais trouver, s'il vous plaît laissez-moi savoir s'il y a une meilleure solution.

Questions connexes