2009-09-01 10 views
42

Mam problem z nagłówkami HTTP, są one kodowane w ASCII i chcę udostępnić widok do pobierania plików, których nazwy mogą nie być ASCII.Jak zakodować nazwę pliku UTF8 dla nagłówków HTTP? (Python, Django)

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

Nie chcę używać statycznych plików służący do tego samego problemu z nazwami plików spoza ASCII, ale w tym przypadku nie byłoby problemu z systemem plików i to kodowanie nazw plików. (Nie wiem, docelowego os.)

Próbowałem już urllib.quote(), ale to podnosi wyjątek KeyError.

Prawdopodobnie robię coś złego, ale może to niemożliwe.

+1

Zdaję sobie sprawę, że jestem spóźniona, ale ... wyjątek KeyError naprawdę mnie trapi. Nie chodzi mi tylko o to, że "od czasu do czasu wpadam na ten problem", mam na myśli, że przesłałem łatkę do Pythona, aby naprawić to wiele lat temu, argumentowałem przez chwilę, po czym zdecydowałem, że nie chcą zmieniać Pythona 2. I naprawiłem ten problem w Pythonie 3, ale nigdy nie zaakceptowałem mojej poprawki w Pythonie 2. Najpierw należy obejść kod .egode ("utf-8"), a następnie użyć adresu urllib.quote. Ale jest to kodowanie URL, które nie jest standardowym sposobem umieszczania ich w nagłówkach. – mgiuca

Odpowiedz

34

To jest najczęściej zadawane pytania.

Nie ma interoperacyjnego sposobu, aby to zrobić. Niektóre przeglądarki implementują własne rozszerzenia (IE, Chrome), inne implementują RFC 2231 (Firefox, Opera).

Zobacz przypadki testowe pod adresem http://greenbytes.de/tech/tc2231/.

Aktualizacja: od listopada 2012 wszystkie aktualne przeglądarki komputerowe obsługują kodowanie zdefiniowane w dokumentach RFC 6266 i RFC 5987 (Safari> = 6, IE> = 9, Chrome, Firefox, Opera, Konqueror).

+0

Dzięki! Najłatwiej znaleźć najłatwiejsze rzeczy;) –

+0

Niedawno Julian przygotował w tym celu profil RFC2231: http://datatracker.ietf.org/doc/draft-reschke-rfc2231-in-http/ –

+4

Opublikowano teraz jako http://greenbytes.de/tech/webdav/rfc5987.html –

30

Nie wysyłaj nazwy pliku w Content-Disposition. Nie ma sposobu, aby parametry nagłówka innego niż ASCII działały w różnych przeglądarkach (*).

Zamiast tego wyślij komunikat "Content-Disposition: attachment" i pozostaw nazwę pliku jako zakodowany w postaci adresu URL ciąg znaków UTF-8 w końcowej części adresu (PATH_INFO) adresu URL, aby przeglądarka domyślnie podniosła i używała . Adresy URL UTF-8 są obsługiwane w przeglądarkach o wiele bardziej niezawodnie niż cokolwiek związanego z Content-Disposition.

(*: faktycznie, nie ma tam nawet obecny standard, który mówi jak to powinny być zrobione jako relacje pomiędzy RFC 2616, 2231 i 2047 są dość dysfunkcyjnych, coś, co Julian stara się wyjaśnić w specyfikacji . Zgodnie poziom wsparcia przeglądarka jest w odległej przyszłości)

+3

Najlepsza odpowiedź zawiera świetne informacje, ale problem został rozwiązany. Dzięki! –

+0

Świetna odpowiedź ... – cherouvim

+7

Od czasu wydania tej odpowiedzi wydano dokument RFC na ten temat. Na uwagę zasługuje konstrukcja 'filename * =', którą obsługują tylko nowsze przeglądarki i która pozwala na korzystanie z kodowania UTF-8, kodowanego zgodnie z RFC 5987. http://tools.ietf.org/html/rfc6266#appendix-D –

0

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 wąchał ogólnie, [te błędne serwery go używają] (http://greenbytes.de/tech/tc2231/#buggy-senders) i są odpowiedzialne za wiele przypadków testowych tc2231/rfc6266. – Tobu

26

Należy zauważyć, że w 2011 roku, RFC 6266 (zwłaszcza Dodatek D) odważa się w tej sprawie i ma konkretnych zaleceń do naśladowania.

Mianowicie, można wydać filename tylko znakami ASCII, a następnie filename* z nazwą w formacie RFC 5987 dla tych agentów, którzy ją rozumieją.

Zazwyczaj będzie to wyglądać filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf, gdzie nazwa pliku Unicode („My Résumé.pdf”) jest kodowana w UTF-8, a następnie kodowane procent (uwaga, NIE używać + dla spacjami).

Proszę rzeczywiście przeczytać RFC 6266 i RFC 5987 (lub użyć solidnej i przetestowanej biblioteki, która to streszcza dla ciebie), ponieważ w moim podsumowaniu brakuje tutaj ważnych szczegółów.

+0

To jest to, czego potrzebowałem do punktu końcowego pobierania pliku w moim projekcie Django. Dziękuję Ci! – macguru2000

2

Mogę powiedzieć, że odniosłem sukces, używając nowszego (RFC 5987) formatu nagłówka zakodowanego za pomocą formularza e-mail (RFC 2231). Wymyśliłem następujące rozwiązanie oparte na kodzie z projektu 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) 

Mam tylko przetestowane mój kod na Python 3.4 z Django 1.8. Więc podobny solution in django-sendfile może lepiej pasować.

W śledzaku Django jest long standing ticket, co potwierdza to, ale nie zaproponowano jeszcze żadnych poprawek. Tak więc, niestety, jest to tak blisko użycia sprawdzonej biblioteki, jak tylko mogłem znaleźć, proszę dać mi znać, czy jest lepsze rozwiązanie.

Powiązane problemy