2009-09-01 16 views
42

Ich habe ein Problem mit HTTP-Headern, sie sind in ASCII codiert und ich möchte eine Ansicht zum Herunterladen von Dateien zur Verfügung gestellt, die Namen nicht ASCII sein können.Wie UTF8 Dateinamen für HTTP-Header zu kodieren? (Python, Django)

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

Ich will nicht statische Dateien für gleiches Problem mit nicht ASCII-Dateinamen, aber in diesem Fall dienen dort verwenden, um ein Problem mit Dateisystem wäre und es ist Dateinamen-Codierung. (Ich weiß nicht Ziel OS.)

Ich habe bereits versucht urllib.quote(), aber es löst KeyError Ausnahme.

Möglicherweise mache ich etwas falsch, aber vielleicht ist es unmöglich.

+1

Ich weiß, dass ich Jahre zu spät bin, aber ... die KeyError-Ausnahme nervt mich wirklich. Ich meine nicht nur "ab und zu stoße ich auf dieses Problem", ich meine, ich habe Python vor einigen Jahren einen Patch zur Verfügung gestellt, der eine Zeitlang argumentierte, dann entschied ich, dass sie Python 2 nicht ändern wollten habe dieses Problem in Python 3 behoben, aber sie haben meinen Patch in Python 2 nie akzeptiert. Der Workaround ist zuerst .encode ('utf-8') und dann urllib.quote. Aber das ist für die URL-Codierung, die nicht die Standardmethode ist, diese in Header zu setzen. – mgiuca

Antwort

34

Dies ist eine FAQ.

Es gibt keine interoperable Möglichkeit, dies zu tun. Einige Browser implementieren proprietäre Erweiterungen (IE, Chrome), andere implementieren RFC 2231 (Firefox, Opera).

Siehe Testfälle unter http://greenbytes.de/tech/tc2231/.

Update: Seit November 2012 unterstützen alle aktuellen Desktop-Browser die in RFC 6266 und RFC 5987 definierte Codierung (Safari> = 6, IE> = 9, Chrome, Firefox, Opera, Konqueror).

+0

Danke! Die einfachsten Dinge sind am schwierigsten zu finden;) –

+0

Vor kurzem hat Julian ein Profil von RFC2231 für diesen Zweck zusammengestellt: http://dataracker.ietf.org/doc/draft-reschke-rfc2231-in-http/ –

+4

Jetzt veröffentlicht as http://greenbytes.de/tech/webdav/rfc5987.html –

30

Senden Sie keinen Dateinamen in Content-Disposition. Es ist nicht möglich, Nicht-ASCII-Headerparameter cross-browser (*) zu verwenden.

Senden Sie stattdessen einfach "Content-Disposition: attachment" und lassen Sie den Dateinamen als URL-codierte UTF-8-Zeichenfolge im nachfolgenden (PATH_INFO) Teil Ihrer URL zurück, damit der Browser sie standardmäßig übernehmen und verwenden kann . UTF-8-URLs werden von Browsern wesentlich zuverlässiger behandelt als alles, was mit Inhaltsdisposition zu tun hat.

(*: Eigentlich ist es nicht einmal ein aktueller Standard, der sagt, wie es sollte wie die Beziehungen zwischen dem RFCs 2616, 2231 und 2047 sind ziemlich dysfunktional getan werden, etwas, das Julian in einem spec versucht aufgeklärt werden . Ebene Konsistente Browser-Unterstützung ist in der fernen Zukunft)

+3

Die obere Antwort enthält einige großartige Informationen, aber Sie haben das Problem tatsächlich gelöst. Vielen Dank! –

+0

Große Antwort ... – cherouvim

+7

Seit dieser Antwort ist ein RFC zu diesem Thema veröffentlicht worden. Bemerkenswert ist das 'filename * =' Konstrukt, das nur neuere Browser unterstützen und es Ihnen garantiert erlauben, UTF-8 zu verwenden, das wie in RFC 5987 kodiert ist. Http://tools.ietf.org/html/rfc6266#appendix-D –

0

Ein 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

User-Agent-Sniffing stinkt im Allgemeinen, [diese Buggy-Server benutzen es] (http://greenbytes.de/tech/tc2231/#buggy-senders) und sind für viele der tc2231/rfc6266-Testfälle verantwortlich. – Tobu

26

Beachten Sie, dass im Jahr 2011, RFC 6266 (insbesondere Anhang D) wogen zu diesem Thema in und spezifische Empfehlungen zu folgen hat.

Nämlich ein filename mit nur ASCII-Zeichen, gefolgt von filename* mit einem RFC 5987-formatierten Dateinamen für die Agenten, die es verstehen.

Typischerweise wird dies aussehen filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf, wo die Unicode-Dateinamen („My Résumé.pdf“) in UTF-8 codiert und dann Prozent-codiert (beachten Sie, tun + für KEINE Leerzeichen verwenden).

Bitte lesen Sie tatsächlich RFC 6266 und RFC 5987 (oder verwenden Sie eine robuste und getestete Bibliothek, die dies für Sie abstrahiert), da meine Zusammenfassung hier in wichtigen Details fehlt.

+0

Dies ist, was ich für einen Datei-Download-Endpunkt in meinem Django-Projekt benötigt habe. Vielen Dank! – macguru2000

2

Ich kann sagen, dass ich Erfolg mit dem neueren (RFC 5987) Format der Angabe eines Headers hatte, die mit dem E-Mail-Formular codiert ist (RFC 2231). Ich habe die folgende Lösung entwickelt, die auf Code aus dem Projekt django-sendfile basiert.

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) 

Ich habe getestet nur meinen Code auf Python 3.4 mit Django 1.8. So kann die ähnliche solution in django-sendfile Ihnen besser passen.

Es gibt eine long standing ticket in Djangos Tracker, die dies bestätigt, aber noch keine Patches vorgeschlagen worden sind. Dies ist leider so nah an der Verwendung einer robusten getesteten Bibliothek, wie ich finden könnte. Bitte lassen Sie mich wissen, ob es eine bessere Lösung gibt.