RFC 2231 section 4 描述了如何为标头值指定要使用的编码而不是 Latin-1。使用标题选项filename*=UTF-8''...,其中... 是url 编码的名称。您还可以包含 filename 选项以提供 Latin-1 后备。
直到最近,浏览器才始终支持这一点。 This page 有一些关于浏览器支持的指标。值得注意的是,IE8 将忽略 UTF-8 选项,如果 UTF-8 选项位于 Latin-1 选项之前,则将完全失败。
Flask 1.0 supports calling send_file with Unicode filenames。如果你使用 Flask 1.0,你可以使用 send_file 和 as_attachment=True 和一个 Unicode 文件名。
from flask import send_file
@app.route('/send-python-report')
def send_python_report():
return send_file('python_report.html', as_attachment=True)
在此之前,您可以使用 Flask 将使用的相同进程手动构建标头。
import unicodedata
from flask import send_file
from werkzeug.urls import url_quote
@app.route('/send-python-report')
def send_python_report():
filename = 'python_report.html'
rv = send_file(filename)
try:
filename = filename.encode('latin-1')
except UnicodeEncodeError:
filenames = {
'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
'filename*': "UTF-8''{}".format(url_quote(filename)),
}
else:
filenames = {'filename': filename}
rv.headers.set('Content-Disposition', 'attachment', **filenames)
return rv
为了安全起见,如果文件名由用户输入提供,则应使用 send_from_directory。过程同上,代入函数。
WSGI 不保证标头选项的顺序,因此如果您想支持 IE8,您必须使用 dump_options_header 和 OrderedDict 完全手动构造标头值。否则,filename* 可能会出现在filename 之前,如上所述,这在 IE8 中不起作用。
from collections import OrderedDict
import unicodedata
from flask import send_file
from werkzeug.http import dump_options_header
from werkzeug.urls import url_quote
@app.route('/send-python-report')
def send_python_report():
filename = 'python_report.html'
rv = send_file(filename)
filenames = OrderedDict()
try:
filename = filename.encode('latin-1')
except UnicodeEncodeError:
filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
else:
filenames['filename'] = filename
rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
return rv