forked from orbit-oss/flask
send_file doesn't allow StringIO
This commit is contained in:
parent
2659f0a5e6
commit
980168d084
3 changed files with 104 additions and 96 deletions
|
|
@ -489,6 +489,11 @@ def send_file(
|
|||
guessing requires a `filename` or an `attachment_filename` to be
|
||||
provided.
|
||||
|
||||
When passing a file-like object instead of a filename, only binary
|
||||
mode is supported (``open(filename, "rb")``, :class:`~io.BytesIO`,
|
||||
etc.). Text mode files and :class:`~io.StringIO` will raise a
|
||||
:exc:`ValueError`.
|
||||
|
||||
ETags will also be attached automatically if a `filename` is provided. You
|
||||
can turn this off by setting `add_etags=False`.
|
||||
|
||||
|
|
@ -499,53 +504,56 @@ def send_file(
|
|||
Please never pass filenames to this function from user sources;
|
||||
you should use :func:`send_from_directory` instead.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
.. versionchanged:: 2.0
|
||||
Passing a file-like object that inherits from
|
||||
:class:`~io.TextIOBase` will raise a :exc:`ValueError` rather
|
||||
than sending an empty file.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
The `add_etags`, `cache_timeout` and `conditional` parameters were
|
||||
added. The default behavior is now to attach etags.
|
||||
.. versionchanged:: 1.1
|
||||
``filename`` may be a :class:`~os.PathLike` object.
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
mimetype guessing and etag support for file objects was
|
||||
deprecated because it was unreliable. Pass a filename if you are
|
||||
able to, otherwise attach an etag yourself. This functionality
|
||||
will be removed in Flask 1.0
|
||||
.. versionchanged:: 1.1
|
||||
Passing a :class:`~io.BytesIO` object supports range requests.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
cache_timeout pulls its default from application config, when None.
|
||||
|
||||
.. versionchanged:: 0.12
|
||||
The filename is no longer automatically inferred from file objects. If
|
||||
you want to use automatic mimetype and etag support, pass a filepath via
|
||||
`filename_or_fp` or `attachment_filename`.
|
||||
|
||||
.. versionchanged:: 0.12
|
||||
The `attachment_filename` is preferred over `filename` for MIME-type
|
||||
detection.
|
||||
.. versionchanged:: 1.0.3
|
||||
Filenames are encoded with ASCII instead of Latin-1 for broader
|
||||
compatibility with WSGI servers.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
UTF-8 filenames, as specified in `RFC 2231`_, are supported.
|
||||
|
||||
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
|
||||
|
||||
.. versionchanged:: 1.0.3
|
||||
Filenames are encoded with ASCII instead of Latin-1 for broader
|
||||
compatibility with WSGI servers.
|
||||
.. versionchanged:: 0.12
|
||||
The filename is no longer automatically inferred from file
|
||||
objects. If you want to use automatic MIME and etag support, pass
|
||||
a filename via ``filename_or_fp`` or ``attachment_filename``.
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
Filename may be a :class:`~os.PathLike` object.
|
||||
.. versionchanged:: 0.12
|
||||
``attachment_filename`` is preferred over ``filename`` for MIME
|
||||
detection.
|
||||
|
||||
.. versionadded:: 1.1
|
||||
Partial content supports :class:`~io.BytesIO`.
|
||||
.. versionchanged:: 0.9
|
||||
``cache_timeout`` defaults to
|
||||
:meth:`Flask.get_send_file_max_age`.
|
||||
|
||||
:param filename_or_fp: the filename of the file to send.
|
||||
This is relative to the :attr:`~Flask.root_path`
|
||||
if a relative path is specified.
|
||||
Alternatively a file object might be provided in
|
||||
which case ``X-Sendfile`` might not work and fall
|
||||
back to the traditional method. Make sure that the
|
||||
file pointer is positioned at the start of data to
|
||||
send before calling :func:`send_file`.
|
||||
.. versionchanged:: 0.7
|
||||
MIME guessing and etag support for file-like objects was
|
||||
deprecated because it was unreliable. Pass a filename if you are
|
||||
able to, otherwise attach an etag yourself. This functionality
|
||||
will be removed in Flask 1.0.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
The ``add_etags``, ``cache_timeout`` and ``conditional``
|
||||
parameters were added. The default behavior is to add etags.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
|
||||
:param filename_or_fp: The filename of the file to send, relative to
|
||||
:attr:`~Flask.root_path` if a relative path is specified.
|
||||
Alternatively, a file-like object opened in binary mode. Make
|
||||
sure the file pointer is seeked to the start of the data.
|
||||
``X-Sendfile`` will only be used with filenames.
|
||||
:param mimetype: the mimetype of the file if provided. If a file path is
|
||||
given, auto detection happens as fallback, otherwise an
|
||||
error will be raised.
|
||||
|
|
@ -620,25 +628,29 @@ def send_file(
|
|||
if current_app.use_x_sendfile and filename:
|
||||
if file is not None:
|
||||
file.close()
|
||||
|
||||
headers["X-Sendfile"] = filename
|
||||
fsize = os.path.getsize(filename)
|
||||
headers["Content-Length"] = fsize
|
||||
data = None
|
||||
else:
|
||||
if file is None:
|
||||
file = open(filename, "rb")
|
||||
mtime = os.path.getmtime(filename)
|
||||
fsize = os.path.getsize(filename)
|
||||
headers["Content-Length"] = fsize
|
||||
elif isinstance(file, io.BytesIO):
|
||||
try:
|
||||
fsize = file.getbuffer().nbytes
|
||||
except AttributeError:
|
||||
# Python 2 doesn't have getbuffer
|
||||
fsize = len(file.getvalue())
|
||||
headers["Content-Length"] = fsize
|
||||
elif isinstance(file, io.TextIOBase):
|
||||
raise ValueError("Files must be opened in binary mode or use BytesIO.")
|
||||
|
||||
data = wrap_file(request.environ, file)
|
||||
|
||||
if fsize is not None:
|
||||
headers["Content-Length"] = fsize
|
||||
|
||||
rv = current_app.response_class(
|
||||
data, mimetype=mimetype, headers=headers, direct_passthrough=True
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue