mirror of https://github.com/pallets/flask.git
Merge pull request #2223 from antlarr/master
Fix send_file's attachment_filename to work with non-ascii filenames
This commit is contained in:
commit
8b45009dbc
|
|
@ -17,6 +17,7 @@ import mimetypes
|
|||
from time import time
|
||||
from zlib import adler32
|
||||
from threading import RLock
|
||||
import unicodedata
|
||||
from werkzeug.routing import BuildError
|
||||
from functools import update_wrapper
|
||||
|
||||
|
|
@ -477,6 +478,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
.. versionchanged:: 0.12
|
||||
The `attachment_filename` is preferred over `filename` for MIME-type
|
||||
detection.
|
||||
|
||||
.. versionchanged:: 0.13
|
||||
UTF-8 filenames, as specified in `RFC 2231`_, are supported.
|
||||
|
||||
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
|
||||
|
||||
:param filename_or_fp: the filename of the file to send in `latin-1`.
|
||||
This is relative to the :attr:`~Flask.root_path`
|
||||
|
|
@ -534,8 +540,22 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
if attachment_filename is None:
|
||||
raise TypeError('filename unavailable, required for '
|
||||
'sending as attachment')
|
||||
headers.add('Content-Disposition', 'attachment',
|
||||
filename=attachment_filename)
|
||||
|
||||
normalized = unicodedata.normalize(
|
||||
'NFKD', text_type(attachment_filename)
|
||||
)
|
||||
|
||||
try:
|
||||
normalized.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
filenames = {
|
||||
'filename': normalized.encode('ascii', 'ignore'),
|
||||
'filename*': "UTF-8''%s" % url_quote(attachment_filename),
|
||||
}
|
||||
else:
|
||||
filenames = {'filename': attachment_filename}
|
||||
|
||||
headers.add('Content-Disposition', 'attachment', **filenames)
|
||||
|
||||
if current_app.use_x_sendfile and filename:
|
||||
if file is not None:
|
||||
|
|
|
|||
|
|
@ -540,10 +540,11 @@ class TestSendfile(object):
|
|||
value, options = \
|
||||
parse_options_header(rv.headers['Content-Disposition'])
|
||||
assert value == 'attachment'
|
||||
assert options['filename'] == 'index.html'
|
||||
assert 'filename*' not in rv.headers['Content-Disposition']
|
||||
rv.close()
|
||||
|
||||
with app.test_request_context():
|
||||
assert options['filename'] == 'index.html'
|
||||
rv = flask.send_file('static/index.html', as_attachment=True)
|
||||
value, options = parse_options_header(rv.headers['Content-Disposition'])
|
||||
assert value == 'attachment'
|
||||
|
|
@ -560,6 +561,19 @@ class TestSendfile(object):
|
|||
assert options['filename'] == 'index.txt'
|
||||
rv.close()
|
||||
|
||||
def test_attachment_with_utf8_filename(self):
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
with app.test_request_context():
|
||||
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=u'Ñandú/pingüino.txt')
|
||||
content_disposition = set(rv.headers['Content-Disposition'].split('; '))
|
||||
assert content_disposition == set((
|
||||
'attachment',
|
||||
'filename="Nandu/pinguino.txt"',
|
||||
"filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"
|
||||
))
|
||||
rv.close()
|
||||
|
||||
def test_static_file(self):
|
||||
app = flask.Flask(__name__)
|
||||
# default cache timeout is 12 hours
|
||||
|
|
|
|||
Loading…
Reference in New Issue