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

6mzjoqzu  于 2023-11-20  发布在  Go
关注(0)|答案(7)|浏览(184)

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

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

字符串
我不想使用非ASCII文件名的静态文件来解决同样的问题,但在这种情况下,文件系统和文件名编码会有问题。(我不知道目标操作系统。)
我已经尝试过urllib.quote(),但它引发了KeyError异常。
也许我做错了什么,但也许这是不可能的。

nkcskrwz

nkcskrwz1#

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

a64a0gku

a64a0gku2#

不要在Content-Disposition中发送文件名。没有办法使非ASCII头参数跨浏览器工作()。
相反,只发送“Content-Disposition:attachment”,并将文件名作为URL编码的UTF-8字符串保留在URL的尾部(PATH_INFO),以便浏览器默认选择和使用。UTF-8 URL在浏览器中的处理比与Content-Disposition有关的任何事情都要可靠得多。
:实际上,甚至没有一个当前的标准来说明它应该如何做,因为RFC 2616,2231和2047之间的关系非常不正常,Julian正试图在规范级别上清理这些东西。

oalqel3c

oalqel3c3#

请注意,在2011年,RFC 6266(特别是附录D)对这个问题进行了权衡,并提出了具体建议。
也就是说,您可以发出一个只包含ASCII字符的filename,然后发出一个包含RFC 5987格式文件名的filename*,以供那些理解它的代理使用。
通常情况下,这看起来像filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf,其中Unicode文件名(“My Résumé.pdf”)被编码为UTF-8,然后被编码(注意,不要对空格使用+)。
请务必阅读RFC 6266和RFC 5987(或者使用一个健壮且经过测试的库来为您抽象这些内容),因为我在这里的总结缺乏重要的细节。

kx1ctssn

kx1ctssn4#

Django 2.1开始(参见issue #16470),您可以使用FileResponse,它将正确设置附件的Content-Disposition头。从Django 3.0开始(issue #30196),它也将正确设置inline文件。
例如,要将MIME类型为image/jpeg的名为my_img.jpg的文件作为HTTP响应返回:

response = FileResponse(open("my_img.jpg", 'rb'), as_attachment=True, content_type="image/jpeg")
return response

字符串
或者,如果你不能使用FileResponse,你可以使用FileResponse源代码中的相关部分来自己设置Content-Disposition头。下面是该源代码的当前外观:

from urllib.parse import quote

disposition = 'attachment' if as_attachment else 'inline'
try:
    filename.encode('ascii')
    file_expr = 'filename="{}"'.format(filename)
except UnicodeEncodeError:
    file_expr = "filename*=utf-8''{}".format(quote(filename))
response.headers['Content-Disposition'] = '{}; {}'.format(disposition, file_expr)

1szpjjfi

1szpjjfi5#

我可以说我已经成功地使用了新的(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的跟踪器中有一个long standing ticket,它承认这一点,但还没有提出任何补丁。所以不幸的是,这是我能找到的最接近使用一个健壮的测试库,如果有更好的解决方案,请让我知道。

eivgtgni

eivgtgni6#

Django的escape_uri_path函数是对我有效的解决方案。
在这里阅读Django文档,查看当前指定的RFC标准。

from django.utils.encoding import escape_uri_path

file = "response.zip"
response = HttpResponse(content_type='application/zip')
response['Content-Disposition'] = f"attachment; filename*=utf-8''{escape_uri_path(file)}"

字符串

z4iuyo4d

z4iuyo4d7#

黑客:

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

字符串

相关问题