我有HTTP标头的问题,它们是用ASCII编码的,我想提供一个视图,用于下载名称可以是非ASCII的文件。
response['Content-Disposition'] = 'attachment; filename="%s"' % (vo.filename.encode("ASCII","replace"), )
我不想使用静态文件来处理与非ASCII文件名相同的问题,但在这种情况下,文件系统及其文件名编码会出现问题。 (我不知道目标操作系统。)
我已经尝试过urllib.quote(),但它引发了KeyError异常。
可能我做错了,但也许这是不可能的。
答案 0 :(得分:36)
这是常见问题。
没有可互操作的方法来做到这一点。一些浏览器实现专有扩展(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)。
答案 1 :(得分:31)
不要在Content-Disposition中发送文件名。没有办法使非ASCII标头参数跨浏览器(*)工作。
相反,只发送“Content-Disposition:attachment”,并将文件名作为URL编码的UTF-8字符串保留在URL的尾随(PATH_INFO)部分,以便浏览器默认选择并使用。浏览器处理UTF-8 URL比使用Content-Disposition更可靠。
(*:实际上,由于RFC 2616,2231和2047之间的关系非常不正常,因此甚至没有一个当前标准说明应该如何完成,这是Julian试图清除的事情在一个规范级别。一致的浏览器支持是在遥远的未来。)
答案 2 :(得分:28)
请注意,在2011年,RFC 6266(特别是附录D)对此问题进行了权衡,并提出了具体建议。
即,您可以发出仅包含ASCII字符的filename
,然后发送filename*
,其中包含RFC 5987格式的文件名,供那些理解它的代理使用。
通常这看起来像filename="my-resume.pdf"; filename*=UTF-8''My%20R%C3%A9sum%C3%A9.pdf
,其中Unicode文件名(“我的Résumé.pdf”)被编码为UTF-8然后进行百分比编码(注意,不要将+
用于空格)。
请实际阅读RFC 6266和RFC 5987(或使用一个强大且经过测试的库,为您提取此内容),因为我的摘要缺乏重要细节。
答案 3 :(得分:5)
从2018年开始, Django 2.1 中提供了一个解决方案(以open ticket贬值了七年之久)。您可以使用FileResponse中内置的as_attachment
参数。例如,要以HTTP响应返回文件类型为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)
答案 4 :(得分: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)
我只使用 Django 1.8 在 Python 3.4 上测试了我的代码。所以类似的solution in django-sendfile可能会让你更好。
Django的跟踪器中有一个long standing ticket,它确认了这一点,但尚未提出补丁。所以不幸的是,我已经找到了一个强大的测试库,如果有更好的解决方案,请告诉我。
答案 5 :(得分:-1)
黑客攻击:
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");
}