Python

Upload a File with Python Flask

Hello in this tutorial, we will create a file upload functionality through python flask and host it on Docker in a containerized environment.

1. Introduction

A common feature of any web application is to provide support to the file upload utility. In python programming, flask provides this functionality where all we need is an HTML form with the encryption level as multipart/form-data. The server-side script will fetch the file from the requests.files[] object and on successful uploading, it will be saved in the desired folder on the server.

1.1 Setting up Python

If someone needs to go through the Python installation on Windows, please watch this link. You can download the Python from this link.

1.2 What is Docker & Setting up Docker

In the present world, Docker is an important term,

  • Often used in CI/CD platform that packages and runs the application with its dependencies inside a container
  • Is a standard for Linux Containers
  • A Container is a runtime that runs under any Linux kernel and provides a private machine-like space under Linux

If someone needs to go through the Docker installation, please watch this video.

2. Upload a File with Python Flask

I am using JetBrains PyCharm as my preferred IDE. You’re free to choose the IDE of your choice. Fig. 1 represents the project structure for this tutorial.

Fig. 1: Application structure

2.1 Creating a requirements file

Add the below code to the requirements file. The file will be responsible to download and install the packages required for this tutorial.

requirements.txt

Flask
Werkzeug

2.2 Basic file upload html form

Define an HTML form in the templates folder with a file field in it. The page will also show the success or error messages from the python flask backend.

upload.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>File upload</title>
    </head>
    <body>
        <h1>Python flask file upload</h1>
        <hr>
        <p>
            {% with messages = get_flashed_messages() %}
              {% if messages %}
                <ul class=flashes>
                {% for message in messages %}
                  <li>{{ message }}</li>
                {% endfor %}
                </ul>
              {% endif %}
            {% endwith %}
        </p>
        <h3>Select a file to upload</h3>
        <form action="/" method="POST" enctype="multipart/form-data">
            <input type="file" name="file" />
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

2.3 Python code

The python file will expose three different endpoints responsible for interacting with the frontend page. We will also add some basic validations such as max file size and allowed file type to the code which can be referenced in the same file.

  • / endpoint – Responsible for showing the HTML page to the user via the HTTP GET call. The same endpoint will also be responsible to handle the HTTP POST call from the form and save the uploaded file in the uploads folder
  • /download endpoint – Responsible to handle the download functionality. I have skipped the download functionality for users to play with the application

main.py

import logging
import os.path

from flask import Flask, render_template, request, redirect, flash
from werkzeug.utils import secure_filename

# [logging config
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(filename)s:%(funcName)s:%(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    level=logging.INFO)
# logging config]


app = Flask(__name__)
app.secret_key = "somesecretkey"

app.config['ALLOWED_EXTENSIONS'] = ['.jpg', '.png']
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024

UPLOAD_FOLDER = os.path.join(os.getcwd(), 'uploads')


@app.route('/', methods=['GET'])
def index():
    logging.info('Showing index page')
    return render_template('upload.html')


@app.route('/', methods=['POST'])
def upload_files():
    logging.info('Starting file upload')

    if 'file' not in request.files:
        flash('No file part')
        return redirect(request.url)

    file = request.files['file']
    # obtaining the name of the destination file
    filename = file.filename
    if filename == '':
        logging.info('Invalid file')
        flash('No file selected for uploading')
        return redirect(request.url)
    else:
        logging.info('Selected file is= [%s]', filename)
        file_ext = os.path.splitext(filename)[1]
        if file_ext in app.config['ALLOWED_EXTENSIONS']:
            secure_fname = secure_filename(filename)
            logging.info('Secure filename is= [%s]', secure_fname)
            file.save(os.path.join(UPLOAD_FOLDER, secure_fname))
            logging.info('Upload is successful')
            flash('File uploaded successfully')
            return redirect('/')
        else:
            logging.info('Invalid file extension')
            flash('Not allowed file type')
            return redirect(request.url)


# Todo - To be implemented.
@app.route('/download', methods=['GET'])
def download():
    return 'Download file'


def check_upload_dir():
    if not os.path.exists(UPLOAD_FOLDER):
        os.makedirs(UPLOAD_FOLDER, exist_ok=True)


if __name__ == '__main__':
    check_upload_dir()

    # Development only: run "python app.py" and open http://localhost:5000
    server_port = os.environ.get('PORT', '5000')
    app.run(debug=False, port=server_port, host='0.0.0.0')

For localhost debugging, you can run the application by running the main.py py file in the PyCharm IDE. The application will be started on the 5000 port number and you can hit the following url – http://localhost:5000 in the browser of your choice to display the HTML page.

3. Setting up requirements for Docker

To deploy this application on Docker we will create two files responsible for having this application up and running in a container. We will also use the .dockerignore file so that we only add the required files to the docker image and the same file can be downloaded from the Downloads section.

3.1 Creating a Dockerfile

Add the below code to the Dockerfile responsible for creating the Docker image. The image name will be driven from the docker-compose.yml file which will be created in the next step.

Dockerfile

# Python image to use
FROM python:3.10-slim

# Author of the image
MAINTAINER danielatlas

# Set the working directory to /app
WORKDIR /app

# Copy the requirements file used for dependencies
COPY requirements.txt .

# Install the needed packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copy rest of the working directory contents into the container at /app
COPY ../upload .

# Run app.py when the container launches
ENTRYPOINT ["python3", "main.py"]

3.2 Creating the docker-compose yml

Add the below code to the docker-compose file responsible for bundling the code, creating the docker image, and starting the container on a port number – 5000 from the created image. You are free to change the details as per your wish but remember that if you change the port number hit the application endpoint on the same port. For this tutorial, I am setting the port number to 5000.

docker-compose.yml

version: "3.7"

services:
  pyflaskfileupload:
    build:
      context: ../upload
      dockerfile: Dockerfile
    image: "pyflaskfileupload:latest"
    container_name: "pyflaskfileupload"
    ports:
      - "5000:5000"

4. Code run & demo

To run the application on the docker container follow the below steps. Open the terminal and navigate to the project directory containing the Dockerfile.

App run

-- to create the image and start container --
docker-compose up -d --build

-- to check if the container is started
docker ps -a

-- to stop and remove the container --
docker-compose down

-- to view the container logs --
docker logs --follow pyflaskfileupload

-- to remove the created image --
docker rmi pyflaskfileupload

Once the up command is successful you can hit the following endpoint – http://localhost:5000 in the browser of your choice and the below page will appear.

Fig. 2: Application demo

An HTML form like in Fig. 2 will be shown to the users and the user can browse the file system for the file and will be uploaded to the server inside the uploads folder. Once the file is uploaded successfully it will be shown in the directory.

Fig. 3: Uploaded file

That is all for this tutorial and I hope the article served you with whatever you were looking for. Happy Learning and do not forget to share!

5. Summary

In this tutorial, we learned about the file upload functionality in a python application using the flask module and hosted the same app on docker. You can download the source code of this tutorial from the Downloads section.

6. Download the Project

This was a tutorial on how to create a python flask file upload functionality and run it in the localhost and dockerized environment.

Download
You can download the full source code of this example here: Upload a File with Python Flask

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button