在Flask响应标头中设置Unicode文件名

5

我正在尝试设置Content-Disposition头来向客户端发送文件。文件名包含Unicode字符。当我尝试设置这个头时,会出现一个UnicodeEncodeError错误。我尝试了各种encodedecode的组合,但都无法使其工作。如何使用Unicode文件名发送文件呢?

destination_file = 'python_report.html'
response.headers['Content-Disposition'] = 'attachment; filename=' + destination_file

  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/server.py", line 495, in send_header
    ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 41-42: ordinal not in range(256)
1个回答

10

RFC 2231第4节中介绍了如何指定一个编码以替代ASCII码用于头部信息。使用头选项filename*=UTF-8''...,其中...是url编码的名称。您还可以包括filename选项,提供一个ASCII码的备选项。

Flask >= 1.0支持使用Unicode文件名调用send_from_directorysend_file函数。您可以使用send_from_directory函数和一个Unicode文件名,并设置参数as_attachment=True以作为附件下载。

from flask import send_from_directory

@app.route("/send-python-report")
def send_python_report():
    return send_from_directory("reports", "python_report.html", as_attachment=True)

为了安全起见,如果文件名由用户输入提供,请确保使用send_from_directory而不是send_file


在 Flask 1.0 之前,您可以手动构建标题,使用Flask 使用的相同过程

import unicodedata
from urllib.parse import quote
from flask import send_from_directory

@app.route('/send-python-report')
def send_python_report():
    filename = "python_report.html"
    rv = send_from_directory("reports", filename)

    try:
        filename.encode("ascii")
    except UnicodeEncodeError:
        simple = unicodedata.normalize("NFKD", filename)
        simple = simple.encode("ascii", "ignore").decode("ascii")
        # safe = RFC 5987 attr-char
        quoted = quote(filename, safe="!#$&+-.^_`|~")
        names = {"filename": simple, "filename*": f"UTF-8''{quoted}"}
    else:
        names = {"filename": filename}

    rv.headers.set("Content-Disposition", "attachment", **names)
    return rv

直到近年来(2017年之前),浏览器并没有始终支持此功能。这个页面列出了一些浏览器支持的度量数据。值得注意的是,IE8将忽略UTF-8选项,并在ASCII选项之前存在UTF-8选项时完全失败。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接