如何为HTTP头编码UTF8文件名?(Python,Django)
我在处理HTTP头的时候遇到了问题,这些头是用ASCII编码的,而我想提供一个下载文件的视图,这些文件的名字可能包含非ASCII字符。
response['Content-Disposition'] = 'attachment; filename="%s"' % (vo.filename.encode("ASCII","replace"), )
我不想用静态文件服务来解决这个非ASCII文件名的问题,但这样的话,文件系统和文件名编码可能会出现问题。(我不知道目标操作系统是什么。)
我已经尝试过使用urllib.quote(),但是它抛出了KeyError异常。
可能是我做错了什么,但也有可能这是不可能实现的。
7 个回答
请注意,在2011年,RFC 6266(特别是附录D)对这个问题进行了讨论,并给出了具体的建议。
简单来说,你可以使用一个只包含ASCII字符的filename
,然后再跟一个filename*
,这个后者是按照RFC 5987格式的文件名,适合那些能理解这个格式的程序。
通常,这个格式看起来像这样: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(或者使用一个经过验证的库来帮你处理这些),因为我在这里的总结缺少了一些重要的细节。
不要在“Content-Disposition”里发送文件名。因为没有办法让非ASCII字符的头部参数在不同浏览器之间正常工作(*).
相反,你只需要发送“Content-Disposition: attachment”,然后把文件名放在URL的最后部分,用URL编码的UTF-8字符串表示,这样浏览器就能自动识别并使用这个文件名。浏览器对UTF-8格式的URL处理得比“Content-Disposition”要可靠得多。
(*: 实际上,目前没有一个标准说明应该怎么做,因为RFC 2616、2231和2047之间的关系相当混乱,Julian正在努力在规范层面上解决这个问题。不同浏览器的支持情况在可预见的未来也不太可能一致。)
这是一个常见问题解答。
目前没有一种通用的方法可以做到这一点。有些浏览器使用了自己特有的扩展功能(比如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也都支持)。