Flask send_file()

Salman Mehmood Jul 27, 2022
Flask send_file()

We will learn, with this explanation, how to upload files to the SQLAlchemy database and then download those files from the database in Flask.

Upload and Download File From the SQLAlchemy Database in Flask

We have a basic Flask app with SQLAlchemy, and we will use the template to upload and download a file from a database. The send_file() function allows us to return a file instead of HTML.

from flask import Flask, request, send_file, render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///db.sqlite3"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)


@app.route("/", methods=["GET", "POST"])
def Main_page():
    return render_template("index.html")

We created a simple form inside the index.html file. We will need to use enctype="multipart/form-data" when working with forms that upload files because if we do not have that, it will not work.

Now, the first thing we will need to do is create a model, which will hold some information about the file and data for the file. We will call this class File_Uploader, which will be an SQLAlchemy model.

This model will need some fields, so we create an id that will be the primary key, and then next, we will add a File_Name, which will be the file name when the user uploads the file through the form. The last is DATA, the file’s raw data stored inside the database.

For this particular data, we need to use a data type called LargeBinary, which allows us to store arbitrary binary data in the database. Keep in mind that typically we do not store files in the database, but we store the metadata in the database.

We could store the files in the database if we do not have many files, or due to limitations, we want to keep everything in the database. You may face performance issues if you combine the file data with other columns in a single table.

You may run into performance issues if this table with the file data gets big, but you can if you want to do it. That’s why we are creating this.

class File_Uploader(db.Model):
    File_ID = db.Column(db.Integer, primary_key=True)
    File_Name = db.Column(db.String(50))
    DATA = db.Column(db.LargeBinary)

We want to take the file from the form and ensure it works. Inside the Main_page() function, we will set the if statement to handle post requests.

Now, we will get files using request.files['file'], store them into the variable, and name it Got_File. This will get all the files sent in the request using the particular key we send from the form.

if request.method == "POST":
    Got_File = request.files["file"]

In the next step, we will upload this to the database, which will use the model that is File_Uploader. We will pass the Got_File.filename to the File_Name field, and then we will pass the file’s data to the DATA field.

The Got_File.read() takes all the binary data for the file and then assigns it to wherever we put it. Then we will add a session and commit the data.

file_upload = File_Uploader(File_Name=Got_File.filename, DATA=Got_File.read())
db.session.add(file_upload)
db.session.commit()
return f"Uploaded: {Got_File.filename}"

Now, we will need to create a database and a table within the database, so we will open up a Python shell in the same directory where our app.py file is located and use the following commands:

from app import db

db.create_all()

Looking at the root directory, we see a database file called db. If we drag this file inside the SQLite Viewer, we can see that the table has been created.

Flask send_file() Output 1

Now, we will run this app and upload a file. Then we will be able to upload the files from the form.

After running the server, we hit the route from where we will access the form to submit the file. We are using these commands to set up the Flask app and run this app.

set FLASK_APP='app.py'
flask run

After uploading files, we can see the files are uploaded successfully, and if we look at the table, we have the id and the file name.

Flask send_file() Output 2

We can upload any file type; it can be an mp3, movie file, or a code file, whatever it is.

Now, we will create a route to download the file that we have uploaded to the database, and we will have a variable in this route called File_ID. Now we will perform the query in the database using that id and get data out of the database for that particular row.

@app.route('/download/<File_ID>')

We must import the BytesIO class from the io module. This will allow us to take the binary data in the database and then convert it to a format Flask can use to regenerate the file.

We will return the file using the send_file() function. Then inside the BytesIO class, we pass the data with the corresponding column.

Then, we pass the attachment_filename keyword argument because we will use an attachment.

def download(File_ID):
    Got_File = File_Uploader.query.filter_by(File_ID=File_ID).first()
    return send_file(
        BytesIO(Got_File.DATA),
        attachment_filename=Got_File.File_Name,
        as_attachment=True,
    )

Let’s take a look at this. We will go to the download page and pass the id; when we hit this route, we can download the file.

Flask send_file() Output 3

Complete Python Code:

from flask import Flask, request, send_file, render_template
from flask_sqlalchemy import SQLAlchemy
from io import BytesIO

app = Flask(__name__)

app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///db.sqlite3"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)


class File_Uploader(db.Model):
    File_ID = db.Column(db.Integer, primary_key=True)
    File_Name = db.Column(db.String(50))
    DATA = db.Column(db.LargeBinary)


@app.route("/", methods=["GET", "POST"])
def Main_page():
    if request.method == "POST":
        Got_File = request.files["file"]

        file_upload = File_Uploader(File_Name=Got_File.filename, DATA=Got_File.read())
        db.session.add(file_upload)
        db.session.commit()

        return f"Uploaded: {Got_File.filename}"
    return render_template("index.html")


@app.route("/download/<File_ID>")
def download(File_ID):
    Got_File = File_Uploader.query.filter_by(File_ID=File_ID).first()
    return send_file(
        BytesIO(Got_File.DATA),
        attachment_filename=Got_File.File_Name,
        as_attachment=True,
    )

The HTML file that we used inside the Flask app:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>File Uploader </title>
    <style>
        body {
          margin: 0;
        }

        .center {
          display: flex;
          justify-content: center;
          align-items: center;
          height: 100vh;
        }
      </style>
</head>
<body>
    <div class="center">
        <form method="POST" action="/" enctype="multipart/form-data">
            <input type="file" name="file">
            <button>Submit</button>
        </form>
    </div>
</body>
</html>
Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

Related Article - Flask File