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.
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 theuploads
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.
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.
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.
You can download the full source code of this example here: Upload a File with Python Flask