Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move send_file and send_from_directory to Werkzeug #3828

Merged
merged 1 commit into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ Unreleased
- Include ``samesite`` and ``secure`` options when removing the
session cookie. :pr:`3726`
- Support passing a ``pathlib.Path`` to ``static_folder``. :pr:`3579`
- ``send_file`` and ``send_from_directory`` are wrappers around the
implementations in ``werkzeug.utils``. :pr:`3828`
- Some ``send_file`` parameters have been renamed, the old names are
deprecated. ``attachment_filename`` is renamed to ``download_name``.
``cache_timeout`` is renamed to ``max_age``. :pr:`3828`
- ``send_file`` passes ``download_name`` even if
``as_attachment=False`` by using ``Content-Disposition: inline``.
:pr:`3828`
- ``send_file`` sets ``conditional=True`` and ``max_age=None`` by
default. ``Cache-Control`` is set to ``no-cache`` if ``max_age`` is
not set, otherwise ``public``. This tells browsers to validate
conditional requests instead of using a timed cache. :pr:`3828`
- ``helpers.safe_join`` is deprecated. Use
``werkzeug.utils.safe_join`` instead. :pr:`3828`


Version 1.1.2
Expand Down
11 changes: 8 additions & 3 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,16 @@ The following configuration values are used internally by Flask:
.. py:data:: SEND_FILE_MAX_AGE_DEFAULT

When serving files, set the cache control max age to this number of
seconds. Can either be a :class:`datetime.timedelta` or an ``int``.
seconds. Can be a :class:`datetime.timedelta` or an ``int``.
Override this value on a per-file basis using
:meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint.
:meth:`~flask.Flask.get_send_file_max_age` on the application or
blueprint.

Default: ``timedelta(hours=12)`` (``43200`` seconds)
If ``None``, ``send_file`` tells the browser to use conditional
requests will be used instead of a timed cache, which is usually
preferable.

Default: ``None``

.. py:data:: SERVER_NAME

Expand Down
38 changes: 17 additions & 21 deletions docs/patterns/fileuploads.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ the file and redirects the user to the URL for the uploaded file::
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
filename=filename))
return redirect(url_for('download_file', name=filename))
return '''
<!doctype html>
<title>Upload new File</title>
Expand Down Expand Up @@ -102,31 +101,28 @@ before storing it directly on the filesystem.
>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'

Now one last thing is missing: the serving of the uploaded files. In the
:func:`upload_file()` we redirect the user to
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
So we write the :func:`uploaded_file` function to return the file of that name. As
of Flask 0.5 we can use a function that does that for us::
We want to be able to serve the uploaded files so they can be downloaded
by users. We'll define a ``download_file`` view to serve files in the
upload folder by name. ``url_for("download_file", name=name)`` generates
download URLs.

.. code-block:: python

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
@app.route('/uploads/<name>')
def download_file(name):
return send_from_directory(app.config["UPLOAD_FOLDER"], name)

Alternatively you can register `uploaded_file` as `build_only` rule and
use the :class:`~werkzeug.wsgi.SharedDataMiddleware`. This also works with
older versions of Flask::
If you're using middleware or the HTTP server to serve files, you can
register the ``download_file`` endpoint as ``build_only`` so ``url_for``
will work without a view function.

from werkzeug.middleware.shared_data import SharedDataMiddleware
app.add_url_rule('/uploads/<filename>', 'uploaded_file',
build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/uploads': app.config['UPLOAD_FOLDER']
})
.. code-block:: python

If you now run the application everything should work as expected.
app.add_url_rule(
"/uploads/<name>", endpoint="download_file", build_only=True
)


Improving Uploads
Expand Down
24 changes: 14 additions & 10 deletions src/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@


def _make_timedelta(value):
if not isinstance(value, timedelta):
return timedelta(seconds=value)
return value
if value is None or isinstance(value, timedelta):
return value

return timedelta(seconds=value)


class Flask(Scaffold):
Expand Down Expand Up @@ -234,13 +235,16 @@ class Flask(Scaffold):
"PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta
)

#: A :class:`~datetime.timedelta` which is used as default cache_timeout
#: for the :func:`send_file` functions. The default is 12 hours.
#: A :class:`~datetime.timedelta` or number of seconds which is used
#: as the default ``max_age`` for :func:`send_file`. The default is
#: ``None``, which tells the browser to use conditional requests
#: instead of a timed cache.
#:
#: This attribute can also be configured from the config with the
#: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration
#: variable can also be set with an integer value used as seconds.
#: Defaults to ``timedelta(hours=12)``
#: Configured with the :data:`SEND_FILE_MAX_AGE_DEFAULT`
#: configuration key.
#:
#: .. versionchanged:: 2.0
#: Defaults to ``None`` instead of 12 hours.
send_file_max_age_default = ConfigAttribute(
"SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta
)
Expand Down Expand Up @@ -297,7 +301,7 @@ class Flask(Scaffold):
"SESSION_COOKIE_SAMESITE": None,
"SESSION_REFRESH_EACH_REQUEST": True,
"MAX_CONTENT_LENGTH": None,
"SEND_FILE_MAX_AGE_DEFAULT": timedelta(hours=12),
"SEND_FILE_MAX_AGE_DEFAULT": None,
"TRAP_BAD_REQUEST_ERRORS": None,
"TRAP_HTTP_EXCEPTIONS": False,
"EXPLAIN_TEMPLATE_LOADING": False,
Expand Down
Loading