1Uploading Files 2=============== 3 4Ah yes, the good old problem of file uploads. The basic idea of file 5uploads is actually quite simple. It basically works like this: 6 71. A ``<form>`` tag is marked with ``enctype=multipart/form-data`` 8 and an ``<input type=file>`` is placed in that form. 92. The application accesses the file from the :attr:`~flask.request.files` 10 dictionary on the request object. 113. use the :meth:`~werkzeug.datastructures.FileStorage.save` method of the file to save 12 the file permanently somewhere on the filesystem. 13 14A Gentle Introduction 15--------------------- 16 17Let's start with a very basic application that uploads a file to a 18specific upload folder and displays a file to the user. Let's look at the 19bootstrapping code for our application:: 20 21 import os 22 from flask import Flask, flash, request, redirect, url_for 23 from werkzeug.utils import secure_filename 24 25 UPLOAD_FOLDER = '/path/to/the/uploads' 26 ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} 27 28 app = Flask(__name__) 29 app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 30 31So first we need a couple of imports. Most should be straightforward, the 32:func:`werkzeug.secure_filename` is explained a little bit later. The 33``UPLOAD_FOLDER`` is where we will store the uploaded files and the 34``ALLOWED_EXTENSIONS`` is the set of allowed file extensions. 35 36Why do we limit the extensions that are allowed? You probably don't want 37your users to be able to upload everything there if the server is directly 38sending out the data to the client. That way you can make sure that users 39are not able to upload HTML files that would cause XSS problems (see 40:ref:`security-xss`). Also make sure to disallow ``.php`` files if the server 41executes them, but who has PHP installed on their server, right? :) 42 43Next the functions that check if an extension is valid and that uploads 44the file and redirects the user to the URL for the uploaded file:: 45 46 def allowed_file(filename): 47 return '.' in filename and \ 48 filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS 49 50 @app.route('/', methods=['GET', 'POST']) 51 def upload_file(): 52 if request.method == 'POST': 53 # check if the post request has the file part 54 if 'file' not in request.files: 55 flash('No file part') 56 return redirect(request.url) 57 file = request.files['file'] 58 # If the user does not select a file, the browser submits an 59 # empty file without a filename. 60 if file.filename == '': 61 flash('No selected file') 62 return redirect(request.url) 63 if file and allowed_file(file.filename): 64 filename = secure_filename(file.filename) 65 file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) 66 return redirect(url_for('download_file', name=filename)) 67 return ''' 68 <!doctype html> 69 <title>Upload new File</title> 70 <h1>Upload new File</h1> 71 <form method=post enctype=multipart/form-data> 72 <input type=file name=file> 73 <input type=submit value=Upload> 74 </form> 75 ''' 76 77So what does that :func:`~werkzeug.utils.secure_filename` function actually do? 78Now the problem is that there is that principle called "never trust user 79input". This is also true for the filename of an uploaded file. All 80submitted form data can be forged, and filenames can be dangerous. For 81the moment just remember: always use that function to secure a filename 82before storing it directly on the filesystem. 83 84.. admonition:: Information for the Pros 85 86 So you're interested in what that :func:`~werkzeug.utils.secure_filename` 87 function does and what the problem is if you're not using it? So just 88 imagine someone would send the following information as `filename` to 89 your application:: 90 91 filename = "../../../../home/username/.bashrc" 92 93 Assuming the number of ``../`` is correct and you would join this with 94 the ``UPLOAD_FOLDER`` the user might have the ability to modify a file on 95 the server's filesystem he or she should not modify. This does require some 96 knowledge about how the application looks like, but trust me, hackers 97 are patient :) 98 99 Now let's look how that function works: 100 101 >>> secure_filename('../../../../home/username/.bashrc') 102 'home_username_.bashrc' 103 104We want to be able to serve the uploaded files so they can be downloaded 105by users. We'll define a ``download_file`` view to serve files in the 106upload folder by name. ``url_for("download_file", name=name)`` generates 107download URLs. 108 109.. code-block:: python 110 111 from flask import send_from_directory 112 113 @app.route('/uploads/<name>') 114 def download_file(name): 115 return send_from_directory(app.config["UPLOAD_FOLDER"], name) 116 117If you're using middleware or the HTTP server to serve files, you can 118register the ``download_file`` endpoint as ``build_only`` so ``url_for`` 119will work without a view function. 120 121.. code-block:: python 122 123 app.add_url_rule( 124 "/uploads/<name>", endpoint="download_file", build_only=True 125 ) 126 127 128Improving Uploads 129----------------- 130 131.. versionadded:: 0.6 132 133So how exactly does Flask handle uploads? Well it will store them in the 134webserver's memory if the files are reasonably small, otherwise in a 135temporary location (as returned by :func:`tempfile.gettempdir`). But how 136do you specify the maximum file size after which an upload is aborted? By 137default Flask will happily accept file uploads with an unlimited amount of 138memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH`` 139config key:: 140 141 from flask import Flask, Request 142 143 app = Flask(__name__) 144 app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 145 146The code above will limit the maximum allowed payload to 16 megabytes. 147If a larger file is transmitted, Flask will raise a 148:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception. 149 150.. admonition:: Connection Reset Issue 151 152 When using the local development server, you may get a connection 153 reset error instead of a 413 response. You will get the correct 154 status response when running the app with a production WSGI server. 155 156This feature was added in Flask 0.6 but can be achieved in older versions 157as well by subclassing the request object. For more information on that 158consult the Werkzeug documentation on file handling. 159 160 161Upload Progress Bars 162-------------------- 163 164A while ago many developers had the idea to read the incoming file in 165small chunks and store the upload progress in the database to be able to 166poll the progress with JavaScript from the client. The client asks the 167server every 5 seconds how much it has transmitted, but this is 168something it should already know. 169 170An Easier Solution 171------------------ 172 173Now there are better solutions that work faster and are more reliable. There 174are JavaScript libraries like jQuery_ that have form plugins to ease the 175construction of progress bar. 176 177Because the common pattern for file uploads exists almost unchanged in all 178applications dealing with uploads, there are also some Flask extensions that 179implement a full fledged upload mechanism that allows controlling which 180file extensions are allowed to be uploaded. 181 182.. _jQuery: https://jquery.com/ 183