Let users download files in flask
Tags: python, flask, tutorial, programming
Published: 2022-03-17 10:48:18

This week I wanted to have some documents that a user landing on my website could download and use. For you, it could be a resume or a cheat sheet, and having that file stored on our server and with a click of a button a user has makes life a whole lot easier. So how do we do it?

So if we bring up your favorite text editor and create a new file called app.py.

So let's set up our virtual env and get flask working. To do this we have to tell python to create a virtual environment at our folder point. 

```
python -m venv env
```

If that works all good you will have to then hope into your virtual environment which based on what OS you are using could be slightly different. 

Windows:
```
myenv\Scripts\activate.bat
```
MAC or Linux
```
source myvenv/bin/activate
```

Next lets import flask 

```
pip install flask
```

With flask installed we need to import it and set up the app.

```python
from flask import (Flask, send_file, url_for, jsonify, render_template)

app = Flask(__name__)


@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
    app.run(debug=True, port=8000, host='127.0.0.1')
```

So what we have here is a basic Flask set-up 
- we import flask and the required modules from the library. 
- We created a new flask instance and add that to the app variable. 
- We created a home page route and render a HTML page
- We created a dunder method to start everything off

Does all that make sense? 

I hope it does! So let's carry on... 

So what do we need to do now? Well, we need a simple HTML file for  a homepage so we can create a button to click for our file. If we create a new folder called templates and in there pop our index.html file. 

```html
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <title>Download Flask!</title>
  </head>
  <body>
    <div class="container">
        <div class="row">
            <h1>Download the file</h1>
            <button class="btn btn-success">Download</button>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
    </body>
</html>
```

So the above is a simple HTML template that's using Bootstrap 5 to give us a simple button.

Ok so lets write the download route

```python
@app.route('/download')
def download():
    path = 'samplefile.pdf'
    return send_file(path, as_attachment=True)
```
So this is our download route and as you can see we set a simple file path and in our return statement we pass the path to a send_file method, we then tell flask we want to send it as an attachment. This will ensure we don't move away from our page and give the user the option of where to store the file.

If we pop back over to our HTML file and find the button and update it to the following:

```html
<a class="btn btn-success" href="{{url_for('download')}}">Download</a>
```

This will allow us to use the new route we just created. URL_FOR will use a keyword to go to our routes and find the route method that matches.

And that my friends is all we need to do, so if you run:

```
python3 app.py
```

For some users it will be:
```
python app.py
```

We will be greeted by our page and the big green download button. If you click the download button you will get your file downloaded to your machine. 

That's not too bad, is it? If you want to see the code you can check it out here: https://github.com/GrahamMorbyDev/download_file