.. _uploading-files:
Uploading Files
===============
Ah yes, the good old problem of file uploads. The basic idea of file
uploads is actually quite simple. It basically works like this:
1. A ``
'''
So what does that :func:`~werkzeug.utils.secure_filename` function actually do?
Now the problem is that there is that principle called "never trust user
input". This is also true for the filename of an uploaded file. All
submitted form data can be forged, and filenames can be dangerous. For
the moment just remember: always use that function to secure a filename
before storing it directly on the filesystem.
.. admonition:: Information for the Pros
So you're interested in what that :func:`~werkzeug.utils.secure_filename`
function does and what the problem is if you're not using it? So just
imagine someone would send the following information as `filename` to
your application::
filename = "../../../../home/username/.bashrc"
Assuming the number of ``../`` is correct and you would join this with
the ``UPLOAD_FOLDER`` the user might have the ability to modify a file on
the server's filesystem he or she should not modify. This does require some
knowledge about how the application looks like, but trust me, hackers
are patient :)
Now let's look how that function works:
>>> 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::
from flask import send_from_directory
@app.route('/uploads/')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
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::
from werkzeug import SharedDataMiddleware
app.add_url_rule('/uploads/', 'uploaded_file',
build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/uploads': app.config['UPLOAD_FOLDER']
})
If you now run the application everything should work as expected.
Improving Uploads
-----------------
.. versionadded:: 0.6
So how exactly does Flask handle uploads? Well it will store them in the
webserver's memory if the files are reasonable small otherwise in a
temporary location (as returned by :func:`tempfile.gettempdir`). But how
do you specify the maximum file size after which an upload is aborted? By
default Flask will happily accept file uploads to an unlimited amount of
memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH``
config key::
from flask import Flask, Request
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
The code above will limit the maximum allowed payload to 16 megabytes.
If a larger file is transmitted, Flask will raise a
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception.
.. admonition:: Connection Reset Issue
When using the local development server, you may get a connection
reset error instead of a 413 response. You will get the correct
status response when running the app with a production WSGI server.
This feature was added in Flask 0.6 but can be achieved in older versions
as well by subclassing the request object. For more information on that
consult the Werkzeug documentation on file handling.
Upload Progress Bars
--------------------
A while ago many developers had the idea to read the incoming file in
small chunks and store the upload progress in the database to be able to
poll the progress with JavaScript from the client. Long story short: the
client asks the server every 5 seconds how much it has transmitted
already. Do you realize the irony? The client is asking for something it
should already know.
An Easier Solution
------------------
Now there are better solutions that work faster and are more reliable. There
are JavaScript libraries like jQuery_ that have form plugins to ease the
construction of progress bar.
Because the common pattern for file uploads exists almost unchanged in all
applications dealing with uploads, there is also a Flask extension called
`Flask-Uploads`_ that implements a full fledged upload mechanism with white and
blacklisting of extensions and more.
.. _jQuery: https://jquery.com/
.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/