如何为HTTP标头编码UTF8文件名?(Python,Django)

Chr*_*ski 45 python django escaping http http-headers

我有HTTP标头的问题,它们用ASCII编码,我想提供一个视图,用于下载名称可以是非ASCII的文件.

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

我不想使用静态文件为非ASCII文件名提供同样的问题,但在这种情况下,文件系统及其文件名编码会出现问题.(我不知道目标操作系统.)

我已经尝试过urllib.quote(),但它引发了KeyError异常.

可能我做错了但也许这是不可能的.

Jul*_*hke 36

这是一个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).


bob*_*nce 31

不要在Content-Disposition中发送文件名.无法使非ASCII标头参数跨浏览器(*)工作.

相反,只发送"Content-Disposition:attachment",并将文件名作为URL编码的UTF-8字符串保留在URL的尾随(PATH_INFO)部分,以便浏览器默认选择和使用.浏览器可以更可靠地处理UTF-8 URL,而不是使用Content-Disposition.

(*:实际上,由于RFC 2616,2231和2047之间的关系非常不正常,因此甚至没有一个当前的标准说明应该如何完成,这是Julian试图在规范级别上清除的东西.一致的浏览器支持是在遥远的未来.)

  • 由于这个答案已经出来,已经发布了关于该主题的RFC.值得注意的是`filename*=`构造,只有较新的浏览器支持并且保证允许您使用UTF-8,如RFC 5987中那样编码.http://tools.ietf.org/html/rfc6266#appendix-D (7认同)
  • 最佳答案包含一些很好的信息,但您实际上已经解决了这个问题.谢谢! (3认同)

Ala*_* H. 29

请注意,在2011年,RFC 6266(特别是附录D)对此问题进行了权衡,并提出了具体的建议.

也就是说,您可以filename只发出一个ASCII字符,然后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(或使用一个强大且经过测试的库,为您提取此内容),因为我的摘要缺乏重要细节.


Wil*_*l S 6

我可以说我已经成功地使用了较新的 ( RFC 5987 ) 格式来指定用电子邮件表单 ( RFC 2231 )编码的标头。我想出了以下基于 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)
Run Code Online (Sandbox Code Playgroud)

我只用Django 1.8Python 3.4上测试了我的代码。因此django-sendfile 中的类似解决方案可能更适合您。

Django 的跟踪器中有一张长期存在的票证,它承认这一点,但尚未提出任何补丁。所以不幸的是,这与我能找到的使用强大的测试库一样接近,如果有更好的解决方案,请告诉我。


Mar*_*ian 5

截至2018年,Django 2.1现在已经有一个解决方案(作为公开票证失去了七年的历史)。您可以使用FileResponse中as_attachment内置的参数。例如,要返回MIME类型的文件作为HTTP响应:output_fileoutput_mime_type

response = FileResponse(open(output_file, 'rb'), as_attachment=True, content_type=output_mime_type)
return response
Run Code Online (Sandbox Code Playgroud)

或者,如果您不能使用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)
Run Code Online (Sandbox Code Playgroud)