2017-04-12 81 views
1

我試圖設置Content-Disposition頭文件將文件發送到客戶端。文件名是Unicode。當我嘗試設置標題時,它以UnicodeEncodeError失敗。我嘗試過encodedecode的各種組合,但無法使其正常工作。我如何發送一個Unicode文件名的文件?在Flask響應頭文件中設置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) 

回答

5

RFC 2231 section 4描述如何指定的編碼來代替Latin-1的的用於一個標頭值。使用標頭選項filename*=UTF-8''...,其中...是網址編碼的名稱。您還可以包含filename選項以提供拉丁-1回退。

直到最近,瀏覽器並沒有一貫支持這一點。 This page有一些關於瀏覽器支持的指標。值得注意的是,IE8將忽略UTF-8選項,並且如果UTF-8選項位於Latin-1選項之前,將會完全失敗。

燒瓶0.13(尚未發佈)將support calling send_file with Unicode filenames。一旦發佈,您可以使用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 
+0

將該溶液現在產生在Chrome 61 – LotusH

+0

一個ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION @LotusH離開了Latin-1的名稱,然後。如果你想報告給Flask,那會很有幫助。 – davidism