Uploading media files to your Flask application
Published: 2021-11-30 09:30:32

In this tutorial, you'll be taken through the easy steps required to upload a media file to your Flask applications. 

##Prerequisite
We'll be building on the simple application built in part 1 of this series. I believe you can recreate that setup by yourself now. 

If you are new to the series, do well to check out part 1  before proceeding. Let's get started!!!

##Configurations
The first thing to do is to create the `Configuration` class that will handle the following

i) location where the uploaded files will be stored
ii) secret key
iii) allowed file extensions
iv) limiting the size of the file that can be uploaded.

### static/uploads directory
Create a directory called `static` in the `core` directory. Then create an `uploads` directory in the static directory. This is where your media files will be stored. You can choose to change the name of the directories if you choose to.

### .env
Since we'll be dealing with forms. We need to ensure that we provide our secret key as an environmental variable to ensure that the application doesn't become vulnerable through the form post requests.

Create a `.env` file in the root repository and place your secret key in it

```javascript
SECRET_KEY = put-your-own-secret-secret-key-here
``` 
### config.py
Create a `config.py` file in the base directory
```javascript
import os
from dotenv import load_dotenv 

load_dotenv()
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
    SECRET_KEY = os.environ.get('SECRET_KEY')
    UPLOAD_FOLDER = os.path.join(basedir, 'core/static/uploads')
    ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
    MAX_CONTENT_LENGTH = 16 * 1024 * 1024
```
The secret key is loaded from the environment file `.env` and assigned to the `SECRET_KEY` variable.

Next, the value assigned to the `UPLOAD_FOLDER` variable is the path to the uploads folder created earlier on.  

The `allowed extensions` coupled with the `maximum content length`(size) of media files that can be uploaded are also specified here.

##__init__.py
Next, you need to import the Configuration class and configure the app variable with it. This assigns all the configuration settings to the application.
```javascript
from flask import Flask
from config import Config

app = Flask(__name__)
app.config.from_object(Config)

from core import views
```

##views.py

```javascript
from flask import render_template,flash,request,redirect
from core import app
from werkzeug.utils import secure_filename
from config import Config
import os

@app.route('/', methods=['POST','GET'])
def index():
    greeting="Hello there, Ace"

    if request.method == 'POST':
        file = request.files['file']
        if file.filename == '':
            flash('No file was selected')
            return redirect(request.url)
        elif file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(Config.UPLOAD_FOLDER, filename))
            flash('Image has been successfully uploaded')
            return redirect('/')
        else:
            flash('Allowed media types are - png, jpg, jpeg, gif')
            return redirect(request.url)
    else:
        return render_template('index.html', greet=greeting)


def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in Config.ALLOWED_EXTENSIONS

```
Let's go through the code above:

The `os` module for path configuration, `Configuration` class, and `flash` function to display messages, `request method & redirect` function to handle page redirects are all imported at the top of the file.

The `secure_filename` function is required to handle unclear or malicious file names. It ensures that the file names are always clean and flat.

The default HTTP method is `GET` so the `POST` method needs to be added to the `app.route` function.

After confirming that a POST request method was made, `request.files['file']` is used to confirm that the data of a `file` input with the name attribute of `file` is included in the submitted form data and an error message is flashed if the condition evaluates to `false` 

The submitted data which is the uploaded media name + extension type is then passed as an argument to the `allowed_file` function where it is split into `name of file` and `extension`. The `allowed_file` function ensures that only a file with the permitted extension is processed and uploaded. 

Next, the `secure_filename` function is called with the filename as an argument. It helps to ensure that a file with a malicious filename is not saved in the application.

If all the checks are passed successfully, the uploaded media file is saved in the specified folder which in your case should be `static/uploads`.

## index.html
To test the file upload functionality, you need to make an upgrade to the index html page as well.
```javascript
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Test HTML</title>
    </head>
    <body>
        <h1>{{greet}}</h1>

		{% with messages = get_flashed_messages() %}
  			{% if messages %}
    			<ul class="flashes">
    			{% for message in messages %}
      				<li>{{ message }}</li>
    			{% endfor %}
    			</ul>
  			{% endif %}
		{% endwith %}

		<form method= "post" enctype=multipart/form-data>
			<input type=file name=file>
			<input type=submit value=Upload>
		</form>
    </body>
</html>

```
The newly added lines surrounded in curly braces allow the `flash` messages to be rendered on the page in case an error occurs while uploading the file.

The [enctype](https://www.w3schools.com/tags/att_form_enctype.asp) attribute assigned the value
`multipart/form-data` is always used when files need to be uploaded via the form. 

The `file` input has the name attribute, `file` which the view function uses to confirm that a file was submitted together with the form.

You can now run your application to test the whole setup:
```javascript
flask run
```
You'll notice that when you try to submit the form without selecting an image, you get an error message notifying you that `No file was selected`.

Then when you finally do the right thing; and you upload an image, you get the message `Image has been successfully uploaded`. Now go to the folder that was created in the static directory `core\static\uploads`. You'll see the image file you just uploaded.

Congratulations, we have come to the end of the tutorial. I believe you can now easily set up your flask applications to have file upload functionality. You can choose to make changes to the setup such as changing the allowed extensions, maximum content length, or the location of your uploads directory. Cheers!!

Bonus: To use the image file, you can store the filename in your database after the image has been stored in the uploads folder, then Query the db for the filename and pass it to the render function.
```javascript
    <img src="{{ url_for('static', filename=upload/'your filename') }}">
``` 
When the page is rendered, `your filename` variable then gets replaced with the result of the db query for the stored `filename` and, your image is displayed.

If you have any questions, feel free to drop them as a comment or send me a message on [Linkedin](https://www.linkedin.com/in/faruq-abdulsalam) or [Twitter](https://twitter.com/Ace_II) and I'll ensure I respond as quickly as I can. Ciao 👋