Flask File Handling Made Easy: Upload and Download Files

Very basic example

code_features

  • Show the user a file upload page
  • Store the uploaded file in the server

This is our first example… Run this code first. Then read the step by step explanation. That will be more effective. Because this tutorial is all about practice. In this article covers simple upload, upload to database, securing the upload by adding limiting file extension and adding download link… Run the example first then read the article…

First Example: Simple Upload

  • copy the example code and run first.
  • The client side HTML is directly added this in python script instead of template for simplicity.
  • It will run on http://127.0.0.1:5000. open that in browser and upload and submit the file, you can get the upload file in the same directory where your python script is.
  • In the next examples, we will store into a folder . And Later into a database.


from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        f.save(f.filename)
        return 'successfully uploaded file'

if __name__ == '__main__':
    app.run(debug=True)

    
    
    

Step by step explanation

  • from flask import Flask, render_template, request
    Import the necessary module

Understanding app = Flask(__name__) for Flask Web Development

When you write app = Flask(__name__), you're creating the core Flask application object. This line is essential in building web applications with Flask. Here's a breakdown of its importance:

Resource Location: The __name__ variable instructs Flask where to locate resources crucial for your web app, such as:
Templates: HTML files that define the structure and appearance of your website.
Static Files: Supporting files like CSS (stylesheets), JavaScript, and images.
Flexible Configurations: Using __name__ allows Flask to adapt configurations based on your development environment (local testing vs. live deployment).

The page where user can upload the file



<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>


    

    






We can add this in templates folder in flask as upload-file.html other names. for simplicity directly included in the python script.
<form action=”/uploader” method=”POST” enctype=”multipart/form-data”>

This HTML file upload form provide a structured mechanism for users to submit files to a web server.

action=”/uploader” : This attribute acts as the destination address for the uploaded file. It directs the web server to a specific processing location (in this case, the “/uploader” route).


method=”POST” : This attribute specifies a secure transfer method. The “POST” method embeds the file within the body of the HTTP request, enhancing data protection.

enctype=”multipart/form-data” : This attribute is essential for file uploads. It establishes an encoding format that seamlessly transmits both files and standard text-based form data.

This code just upload the file in the server and store the in the same directory. In the next example We will learn how to store the file on specific directory.




@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        f.save(f.filename)
        return 'successfully uploaded file'

    
    


Second Example: Storing the uploaded files in a specific folder



from flask import Flask, render_template, request
import os

app = Flask(__name__)

UPLOAD_FOLDER = 'user-uploads'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)
        f.save(file_path)
        return 'File uploaded successfully'

if __name__ == '__main__':
    app.run(debug=True)



    
    
    

We have created a folder named ‘upload files’ . If not created the code wil automatically create the folder



import os
UPLOAD_FOLDER = 'user-uploads'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

    
    

We applied python os module to create the folder. 

import os

Integrates the 'os' module, enabling operating system's file management capabilities.



UPLOAD_FOLDER = 'uploads'
keyword-relevant name ('uploads') for the directory where uploaded files will be stored.

if not os.path.exists(UPLOAD_FOLDER):
Verifies the existence of the 'uploads' directory.

os.makedirs(UPLOAD_FOLDER)
If the 'uploads' directory is absent, this command generates it along with any necessary subdirectories.


app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
Define the configuration variable 
Imagine UPLOAD_FOLDER as a designated storage location within your Flask application for uploaded files. The line app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER does the following:

Assigns a Name: It gives this storage location a clear label, 'UPLOAD_FOLDER'.
Defines a Path: It links this label to the actual folder path on your system where uploaded files will be saved.

In the next example we will learn to store the uploaded files in the database.


Third Example: Storing the uploaded files in a database

Storing user data securely and effectively is crucial for many applications. A database is the optimal solution for long-term storage. SQL databases like SQLite, PostgreSQL, and MySQL are excellent options, offering varying features to suit your needs.

For enhanced security and flexibility, consider using an object-relational mapper (ORM). ORMs streamline database interactions within your code. In this example, we’ll demonstrate how to utilize the Peewee ORM with Python’s built-in SQLite database. This approach allows for effortless migration to other SQL databases in the future.

Install peewee orm

pip install peewee

And now copy , run and test the code..



from flask import Flask, render_template, request
from peewee import SqliteDatabase, Model, CharField
from werkzeug.utils import secure_filename
import os

app = Flask(__name__)

# SQLite database initialization
db = SqliteDatabase('uploads.db')

# Define the model for file uploads
class Upload(Model):
    filename = CharField()
    filepath = CharField()

    class Meta:
        database = db

# Create tables if they don't exist
db.connect()
db.create_tables([Upload], safe=True)
db.close()

@app.route('/')
def upload_file():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Upload</title>
</head>
<body>
    <h2>Upload a File</h2>
    <form action="/uploader" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit"/>
    </form>
</body>
</html>
    '''

# Save file and file information to database
@app.route('/uploader', methods=['POST'])
def upload_file_post():
    if request.method == 'POST':
        f = request.files['file']
        if f:
            filename = secure_filename(f.filename)
            file_path = os.path.join('upload files', filename)
            f.save(file_path)

            # Save file info to database
            Upload.create(filename=filename, filepath=file_path)

            return 'File uploaded successfully'

if __name__ == '__main__':
    app.run(debug=True)


    
    
    

Fourth Example: Securing the The file upload by limiting the upload extensions

Though we can tell the user which type of files to upload. But we can not relay only on that. So for more security and specially server and other users security we must limit the upload files extensions. which files are allowed by the server.



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

UPLOAD_FOLDER = 'user-uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = 'your_secret_key_here'


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


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # If the user does not select a file, the browser submits an
        # empty file without a filename.
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash('File uploaded successfully')
            return redirect(url_for('upload_file'))
        else:
            flash('Invalid file type. Allowed types are: png, jpg, jpeg, gif, bmp')
            return redirect(request.url)
    return render_template('upload.html')


if __name__ == '__main__':
    app.run(debug=True)



    
    
    

Fifth Example: Create link to the file and add downloading option

In this example we will show the download link to the user and make the filename very unique.
In the previous examples we directly added html code in the python script. When application becomes larger and complex its essential to handle everything separately. In this example we will use flask render_temple to render the html

So this example structure

flask_app/
│
├── app.py
│
└── templates/
    └── upload-page.html

Copy, run and test the code.



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

UPLOAD_FOLDER = 'user-uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'


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


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        
        file = request.files['file']
        
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash('File uploaded successfully')
            return redirect(url_for('upload_file'))
        
        flash('Invalid file type. Allowed types are: png, jpg, jpeg, gif, bmp')
        return redirect(request.url)

    return render_template('upload-page.html')


if __name__ == '__main__':
    app.run(debug=True)



    
    
    

The upload-page.html




<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>File Upload</title>
</head>
<body>
	<h2>Upload a File</h2>
	<form action="/" method="POST" enctype="multipart/form-data">
		<input type="file" name="file" />
		<input type="submit"/>
	</form>
	{% with messages = get_flashed_messages() %}
		{% if messages %}
			<ul>
			{% for message in messages %}
				<li>{{ message }}</li>
			{% endfor %}
			</ul>
		{% endif %}
	{% endwith %}
</body>
</html>



    
    
    

In the example we did not use database we stored the uploaded data in a folder. . But in previous example we have the database example( Third example) ..Then why not apply our previous learning experience of database here. Try it by yourself.