如何编码UTF8文件名的HTTP标头? (Python中,Django的)(How to enc

2019-06-21 08:56发布

我有问题,HTTP头,他们在ASCII编码,我想提供一个视图下载文件名称可以是非ASCII。

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

我不想用非ASCII文件名服务于同样的问题静态文件,但在这种情况下,将与文件系统的一个问题,它的文件名编码。 (我不知道目标OS)。

我已经尝试过urllib.quote(),但它会引发KeyError异常例外。

也许我做错了什么,但也许这是不可能的。

Answer 1:

这是一个常见问题。

有没有互操作的方式来做到这一点。 有些浏览器实现专有扩展(IE,Chrome浏览器),其他实现RFC 2231(火狐,歌剧)。

见测试用例在http://greenbytes.de/tech/tc2231/ 。

更新:如2012年11月的,目前所有的桌面浏览器支持在RFC 6266和RFC 5987中定义的编码(野生> = 6,IE> = 9,铬,Firefox,歌剧,Konqueror中)。



Answer 2:

不要在内容处理发送的文件名。 有没有办法让非ASCII头参数工作跨浏览器(*)。

相反,仅发送“内容处置:附件”,并留下文件名作为网址的结尾(PATH_INFO)部分的URL编码的UTF-8字符串,浏览器回暖,默认情况下使用。 UTF-8网址是更加可靠的浏览器比任何与内容处置处理。

(*:其实,甚至有不是一个现行的标准,说应该如何作为的RFC 2616,2231和2047是非常不正常的关系,一些朱利安试图在规范层面得到清理完成一致的浏览器支持。在遥远的未来。)



Answer 3:

需要注意的是,2011年, RFC 6266 (特别是附录d)在权衡对这个问题,并有具体的建议遵循。

也就是说,你可以发出一个filename ,只有ASCII字符,然后filename*与那些理解代理RFC 5987格式的文件名。

通常,这会看起来像filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf ,其中的Unicode文件名( “我的Résumé.pdf”)被编码成UTF-8,然后百分比编码(注意,不使用+为空格)。

请不要实际阅读RFC 6266和RFC 5987(或使用抽象这个给你一个强大的和测试库),这里是缺乏重要的细节,我的总结。



Answer 4:

我可以说,我已经使用了较新的(成功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)

我只测试我的代码上的Python 3.4Django 1.8。 因此,类似的Django的sendfile的解决方案可以更好地适合你。

有一个长期的票在Django的追踪器承认这一点,但没有补丁尚未AFAICT建议。 所以,不幸的是这是接近使用一个强大的测试库,我能找到,请让我知道,如果有更好的解决方案。



Answer 5:

截至2018年,一个解决方案是目前在Django 2.1(含情脉脉七年作为一个后可用公开售票 )。 您可以使用as_attachment内置参数FileResponse 。 例如,为了返回一个文件output_file MIME类型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)


Answer 6:

黑客:

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");
}


文章来源: How to encode UTF8 filename for HTTP headers? (Python, Django)