在开发基于python-flask框架的网站的下载功能时,遇到了个大坑:
虽然flask中自带了文件下载的相关函数,但这些函数在下载文件名中带有中文的文件(如“测试文件.zip”)时,都会报错,出现404等情况。
百度上有很多种该问题的解决方案,但经过我测试,很多的解决方案在新版flask(0.12.2)+python3.6的环境中根本不好使。研究了一个早上,终于找到了终极解决方案:
@app.route('/file/download/<filename>', methods=['GET'])
def file_download(filename):
filename=FILE_PATH+filename
response = make_response(send_file(filename))
basename = os.path.basename(filename)
response.headers["Content-Disposition"] = \
"attachment;" \
"filename*=UTF-8''{utf_filename}".format(
utf_filename=quote(basename.encode('utf-8'))
)
return response
解决该问题的关键点在于对header中Content-Disposition的处理:
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。当 Internet Explorer 接收到头时,它会激活文件下载对话框,它的文件名框自动填充了头中指定的文件名。(请注意,这是设计导致的;无法使用此功能将文档保存到用户的计算机上,而不向用户询问保存位置。)
服务端向客户端游览器发送文件时,如果是浏览器支持的文件类型,一般会默认使用浏览器打开,比如txt、jpg等,会直接在浏览器中显示,如果需要提示用户保存,就要利用Content-Disposition进行一下处理。因此我们在header中加入了attachment这一项
此外,flask在处理Content-Disposition的时候,并没有把文件名编码设置成我们常用的UTF-8格式,因此我们需要自己在Content-Disposition中,把文件名设置成UTF-8格式。最后使用 urllib3 库中的(from urllib.parse import quote)中的quote, 对文件名进行编码。
经过这一系列操作,用户就可以下载服务器上文件名中带有中文的文件,而且保存后就是文件名也是中文的了,不会出现乱码。