2009-09-01 5 views
42

Ho un problema con le intestazioni HTTP, sono codificate in ASCII e voglio fornire una vista per scaricare i file che i nomi possono essere non ASCII.Come codificare il nome file UTF8 per le intestazioni HTTP? (Python, Django)

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

Non voglio usare file statici che servono per lo stesso problema con i nomi di file non ASCII ma in questo caso ci sarebbe un problema con il sistema di file ed è il nome del file di codifica. (Non conosco il sistema operativo di destinazione.)

Ho già provato urllib.quote(), ma solleva un'eccezione KeyError.

Forse sto facendo qualcosa di sbagliato ma forse è impossibile.

+1

Mi rendo conto che sono in ritardo di anni, ma ... l'eccezione KeyError mi infastidisce davvero. Non intendo solo "ogni tanto mi imbatto in questo problema", voglio dire, ho inviato una patch a Python per risolvere questo problema anni fa, ho discusso per un po ', poi ho deciso che non volevo cambiare Python 2. I ha risolto questo problema in Python 3, ma non hanno mai accettato la mia patch in Python 2. La soluzione è prima di .encode ('utf-8'), quindi utilizzare urllib.quote. Ma questo è per la codifica dell'URL che non è il modo standard per inserire queste intestazioni. – mgiuca

risposta

34

Questa è una FAQ.

Non esiste un modo interoperabile per eseguire questa operazione. Alcuni browser implementano estensioni proprietarie (IE, Chrome), altri implementano RFC 2231 (Firefox, Opera).

Vedere casi di test a http://greenbytes.de/tech/tc2231/.

Aggiornamento: a partire da novembre 2012, tutti gli attuali browser desktop supportano la codifica definita in RFC 6266 e RFC 5987 (Safari> = 6, IE> = 9, Chrome, Firefox, Opera, Konqueror).

+0

Grazie! Più facili sono le cose più difficili da trovare;) –

+0

Più recentemente, Julian ha creato un profilo di RFC2231 per questo scopo: http://datatracker.ietf.org/doc/draft-reschke-rfc2231-in-http/ –

+4

Ora pubblicato come http://greenbytes.de/tech/webdav/rfc5987.html –

30

Non inviare un nome file in Content-Disposition. Non esiste alcun modo per far funzionare i parametri di intestazione non ASCII cross-browser (*).

Invece, invia solo "Content-Disposition: attachment" e lascia il nome file come una stringa UTF-8 con codifica URL nella parte finale (PATH_INFO) dell'URL, affinché il browser possa essere prelevato e utilizzato per impostazione predefinita . Gli URL UTF-8 sono gestiti in modo molto più affidabile dai browser rispetto a qualsiasi cosa abbia a che fare con Content-Disposition.

(*: in realtà, non c'è nemmeno uno standard corrente che dice come dovrebbe essere fatto come i rapporti tra RFC 2616, 2231 e 2047 sono abbastanza disfunzionale, qualcosa che Giuliano sta cercando di ottenere chiarito in una specifica . livello di supporto del browser costante è in un lontano futuro)

+3

La risposta in alto contiene alcune ottime informazioni, ma in realtà hai risolto il problema. Grazie! –

+0

Grande risposta ... – cherouvim

+7

Da quando è stata resa disponibile questa risposta, è stato emesso un RFC su questo argomento. Di nota è il costrutto 'filename * =' che supporta solo i browser più recenti ed è garantito per consentire di utilizzare UTF-8, codificato come in 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

Lo sniffing user-agent in generale fa schifo, [questi buggy server lo usano] (http://greenbytes.de/tech/tc2231/#buggy-senders) e sono responsabili di molti dei casi di test tc2231/rfc6266. – Tobu

26

nota che nel 2011, RFC 6266 (soprattutto Appendice D) pesato su questo tema e ha raccomandazioni specifiche da seguire.

In particolare, è possibile emettere uno filename con solo caratteri ASCII, seguito da filename* con un nome file in formato RFC 5987 per gli agenti che lo capiscono.

In genere questo sarà simile filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf, in cui il nome del file Unicode ("Il mio Résumé.pdf") è codificato in UTF-8 e poi cento-codificato (nota, NON utilizzare + per gli spazi).

Si prega di leggere effettivamente RFC 6266 e RFC 5987 (o utilizzare una libreria robusta e testata che ne riassuma questo per voi), in quanto il mio riassunto qui manca di dettagli importanti.

+0

Questo è ciò di cui avevo bisogno per un endpoint per il download di file nel mio progetto Django. Grazie! – macguru2000

2

Posso dire che ho avuto successo utilizzando il formato più recente (RFC 5987) di specificare un'intestazione codificata con il modulo di posta elettronica (RFC 2231). Ho trovato la seguente soluzione basata sul codice del progetto 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) 

Ho testato solo il mio codice su Python 3.4 con Django 1.8. Quindi lo stesso solution in django-sendfile potrebbe farti meglio.

C'è uno long standing ticket nel tracker di Django che riconosce ciò ma non è stato ancora proposto alcun patch afaict. Quindi sfortunatamente questo è il più vicino possibile all'utilizzo di una solida libreria testata, per favore fammi sapere se c'è una soluzione migliore.

Problemi correlati