【问题标题】:Set Unicode filename in Flask response header在 Flask 响应头中设置 Unicode 文件名
【发布时间】:2017-09-08 00:06:12
【问题描述】:

我正在尝试设置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)

【问题讨论】:

    标签: python python-3.x flask http-headers


    【解决方案1】:

    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_fileas_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_headerOrderedDict 完全手动构造标头值。否则,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
    

    【讨论】:

      猜你喜欢
      • 2014-11-09
      • 2015-08-23
      • 2020-10-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-07
      • 2018-09-01
      • 1970-01-01
      相关资源
      最近更新 更多