web-dev-qa-db-ja.com

HTTPヘッダーのUTF8ファイル名をエンコードする方法(Python、Django)

HTTPヘッダーに問題があり、ASCIIでエンコードされています。名前をASCII以外にすることができるファイルをダウンロードするためのビューを提供したいと思います。

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

ASCII以外のファイル名で同じ問題に対応する静的ファイルを使用したくないのですが、この場合、ファイルシステムとファイル名のエンコーディングに問題があります(私はしません)ターゲットOSを知っている。)

Urllib.quote()をすでに試しましたが、KeyError例外が発生します。

たぶん私は何か間違ったことをしていますが、おそらくそれは不可能です。

45

これはFAQです。

これを行う相互運用可能な方法はありません。一部のブラウザーは独自の拡張機能(IE、Chrome)を実装し、他のブラウザーはRFC 2231(Firefox、Opera)を実装しています。

http://greenbytes.de/tech/tc2231/ でテストケースを参照してください。

更新:2012年11月の時点で、現在のすべてのデスクトップブラウザーはRFC 6266およびRFC 5987(Safari> = 6、IE> = 9、Chrome、Firefox、Opera、Konqueror)で定義されたエンコーディングをサポートしています。

36
Julian Reschke

Content-Dispositionでファイル名を送信しないでください。非ASCIIヘッダーパラメータをクロスブラウザ(*)で機能させる方法はありません。

代わりに、「Content-Disposition:attachment」のみを送信し、ブラウザがデフォルトで取得して使用できるように、ファイル名をURLの末尾(PATH_INFO)の部分にURLエンコードされたUTF-8文字列のままにします。 UTF-8 URLは、Content-Dispositionを使用する場合よりも、ブラウザーによってはるかに確実に処理されます。

(*:実際には、それをどのように記述するかという現在の標準さえありませんすべき RFC 2616、2231、および2047の間の関係はかなり機能不全になっているので、ジュリアンが仕様レベル。一貫性のあるブラウザのサポートは遠い将来です。)

31
bobince

2011年に RFC 6266 (特に付録D)がこの問題を検討し、従うべき特定の推奨事項があることに注意してください。

つまり、ASCII文字のみの後にfilenameを発行し、その後にfilename*をRFC 5987形式のファイル名とともに発行して、それを理解するエージェントに発行できます。

通常、これはfilename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdfのようになり、Unicodeファイル名( "MyRésumé.pdf")はUTF-8にエンコードされてからパーセントエンコードされます(スペースには+を使用しないでください)。

RFC 6266とRFC 5987を実際に読んでください(または、これを抽象化する堅牢でテスト済みのライブラリを使用してください)。私の概要には重要な詳細が欠けているためです。

29
Alan H.

2018年の時点で、Django 2.1でソリューションが利用可能になりました( オープンチケット として7年間衰退した後)。 FileResponse に組み込まれているas_attachmentパラメータを使用できます。たとえば、MIMEタイプoutput_fileのファイルoutput_mime_typeをHTTP応答として返すには、次のようにします。

response = FileResponse(open(output_file, 'rb'), as_attachment=True, content_type=output_mime_type)
return response

または、FileResponseを使用できない場合は、ソースから関連する部分を使用して、Content-Dispositionをさらに直接変更できます。現在、そのソースは次のようになっています。

from urllib.parse import quote
try:
    document.file_name.encode('ascii')
    file_expr = 'filename="{}"'.format(filename)
except UnicodeEncodeError:
    # Handle a non-ASCII filename
    file_expr = "filename*=utf-8''{}".format(quote(filename))
response['Content-Disposition'] = 'attachment; {}'.format(file_expr)
6
Mark Chackerian

電子メールフォーム( RFC 2231 )でエンコードされたヘッダーを指定する新しい( RFC 5987 )形式を使用して成功したと言えます。 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)

Python 3.4Django 1.8でコードをテストしただけです。したがって、同様の Django-sendfileのソリューション が適している可能性があります。

Djangoのトラッカーには 長期チケット があり、これを認めていますが、パッチはまだ提案されていません。残念ながら、これは私が見つけた堅牢なテスト済みライブラリの使用に近いので、もっと良い解決策があるかどうか知らせてください。

4
Will S