How to Serve Static Files in Flask

How to serve static files in Flask

In production, configure the HTTP server (Nginx, Apache, etc.) in front of your application to serve requests to /static from the static folder. A dedicated web server is very good at serving static files efficiently, although you probably won't notice a difference compared to Flask at low volumes.

Flask automatically creates a /static/<path:filename> route that will serve any filename under the static folder next to the Python module that defines your Flask app. Use url_for to link to static files: url_for('static', filename='js/analytics.js')

You can also use send_from_directory to serve files from a directory in your own route. This takes a base directory and a path, and ensures that the path is contained in the directory, which makes it safe to accept user-provided paths. This can be useful in cases where you want to check something before serving the file, such as if the logged in user has permission.

from flask import send_from_directory

@app.route('/reports/<path:path>')
def send_report(path):
return send_from_directory('reports', path)

Do not use send_file or send_static_file with a user-supplied path. This will expose you to directory traversal attacks. send_from_directory was designed to safely handle user-supplied paths under a known directory, and will raise an error if the path attempts to escape the directory.

If you are generating a file in memory without writing it to the filesystem, you can pass a BytesIO object to send_file to serve it like a file. You'll need to pass other arguments to send_file in this case since it can't infer things like the file name or content type.

Flask: How to serve static html?

Reducing this to the simplest method that'll work:

  1. Put static assets into your static subfolder.
  2. Leave Flask set to the default, don't give it a static_url_path either.
  3. Access static content over the pre-configured /static/ to verify the file works

If you then still want to reuse a static file, use current_app.send_static_file(), and do not use leading / slashes:

from flask import Flask, current_app
app = Flask(__name__)

@app.route('/')
def hello_world():
return current_app.send_static_file('editor.html')

This looks for the file editor.html directly inside the static folder.

This presumes that you saved the above file in a folder that has a static subfolder with a file editor.html inside that subfolder.

Some further notes:

  • static_url_path changes the URL static files are available at, not the location on the filesystem used to load the data from.
  • render_template() assumes your file is a Jinja2 template; if it is really just a static file then that is overkill and can lead to errors if there is actual executable syntax in that file that has errors or is missing context.

How to serve static site with flask

We can consider MkDocs's rendered HTML pages as simple Flask templates. So all we have to do is to create a Flask endpoint that first do the permissions-checking then based on the URL, serves the rendered MkDocs HTML file or a related static file.

Let's call our new endpoint bridge. First, put everything from MkDocs site directory to Flask's templates/bridge directory. My example MkDocs tree looks like this:

$ tree templates/bridge
templates/bridge
├── 404.html
├── css
│   ├── base.css
│   ├── bootstrap.min.css
│   └── font-awesome.min.css
├── dir1
│   ├── sub1
│   │   └── index.html
│   └── sub2
│   └── index.html
├── dir2
│   ├── sub1
│   │   └── index.html
│   └── sub2
│   └── index.html
├── fonts
│   ├── fontawesome-webfont.eot
│   ├── fontawesome-webfont.svg
│   ├── fontawesome-webfont.ttf
│   ├── fontawesome-webfont.woff
│   └── fontawesome-webfont.woff2
├── img
│   ├── favicon.ico
│   └── grid.png
├── index.html
├── js
│   ├── base.js
│   ├── bootstrap.min.js
│   └── jquery-1.10.2.min.js
├── search
│   ├── lunr.js
│   ├── main.js
│   ├── search_index.json
│   └── worker.js
├── sitemap.xml
└── sitemap.xml.gz

And our new Flask bridge endpoint:

from flask import Flask, render_template, send_from_directory

app = Flask(__name__)

@app.route('/bridge/')
@app.route('/bridge/<path:p1>/')
@app.route('/bridge/<path:p1>/<path:p2>/')
@app.route('/bridge/<path:p1>/<path:p2>/<path:p3>/')
def bridge(p1=None, p2=None, p3=None):
# Permissions checking...

# Serve MkDocs's static files requested from CSS files
if p1 == 'css' and p2 in ('img', 'fonts'):
# CSS fix, e.g. /bridge/css/img/example.png -> /bridge/img/example.png
return send_from_directory(f'templates/bridge/{p2}/', p3)

# Serve MkDocs's static files
if p1 in ('css', 'js', 'fonts', 'search'):
return send_from_directory(f'templates/bridge/{p1}/', p2)

# Serve rendered MkDocs HTML files
if p3 != None:
template = f'bridge/{p1}/{p2}/{p3}/index.html'
elif p2 != None:
template = f'bridge/{p1}/{p2}/index.html'
elif p1 != None:
template = f'bridge/{p1}/index.html'
else:
template = 'bridge/index.html'

return render_template(template)

As you see we created a couple of path definitions. Since MkDocs uses relative paths everywhere, the URL path dir1/sub1/ in MkDocs will become https://yoursite.com/bridge/dir1/sub1/ so we can catch them with this URL routing scheme and the URL parts will land into p1, p2, p3 path variables that we will use to serve the corresponding content.

There are two types of content: static files (e.g. CSS files or images) and HTML content files. The static files will be in the css, js, images, fonts, etc. directory, so when p1 equals one of these we use Flask's send_from_directory function to serve them. Static files referenced from CSS files need to have a specific treatment because MkDocs uses relative paths there as well.

And for the rendered index.html files we just need to determine the nesting level based on the path and return the selected index.html file as a normal Flask template. Since MkDocs uses relative URLs we don't have to modify anything in the rendered HTML files, every URL will receive the /bridge/ prefix, so we can serve them with our bridge endpoint.

You should do the permissions checking at the beginning of bridge (or as a decorator, depending your solution). You may need to add p4 and/or p5 path variables if you have more deeply nested content, but it's a straightforward extension of my example.

Note: the 404 error page will be also served by Flask.

How to properly server static files from a Flask server?

Pass these arguments when you initialise the app:

app = Flask(__name__, static_folder='public',
static_url_path='frontend_public' )

This would make the file public/blah.txt available at http://example.com/frontend_public/blah.txt.

  • static_folder sets the folder on the filesystem
  • static_url_path sets the path used within the URL

If neither of the variables are set, it defaults to 'static' for both.

Hopefully this is what you're asking.

How to send a file from a network drive in Flask

All static files must be in static folder. Some oprions you have:

  1. Copy the files that are elsewhere on your filesystem into that folder
  2. Add some code to your server script that copies the files from their origin to the static files folder
  3. Put your server script on your external drive, so your static folder is on that drive, and run it from there. If you need to serve content from several folders on that drive you might be able to create shortcuts to those folders under static folder but I can't test right now , on Windows I doubt this will work (on Linux very likely that symbolic links would work).
  4. You could have one flask app running for each different folder where you have static content, listening on a different port. Those apps would all be identical. Your HTML served by your main app would have links with those ports.
  5. Find where in the source flask looks for the source folder, and extend by a few lines of code to be able to look in other folders.
  6. Use flask.send_file and flask.send_file_from_directory: as long as your database contains a mapping of file ID's to file paths, and you put the file ID's in the HTML served to client, then when client requests the file, your flask app uses the ID in the URL to determine file location and uses flask.send_file_from_directory to serve the file.
  7. You might also be able to use Flask Blueprints since each blueprint can have its own static folder

Serving static files from static folder with Flask

If you have audio files in the static folder you can access them in templates using url_for as like other static files. Documentation of serving static files can be found in this URL.

Here I am showing an example of getting audio files URL in templates. I have passed the list of files from Flask app to the templates.

Directory structure:

├── app.py
├── static
│   ├── demo1.mp3
│   ├── demo2.mp3
│   └── demo3.mp3
└── templates
├── audio.html

app.py:

from flask import Flask, render_template


app = Flask(__name__, static_folder='static')

@app.route('/')
def index():
audio_files = ["demo1.mp3", "demo2.mp3", "demo3.mp3"]
return render_template('audio.html', audio_files=audio_files)

audio.html:

<!DOCTYPE html>
<html>
<head>
<title>Audio Example</title>
</head>
<body>
<h2>Serving audio files from Flask</h2>
{% for file in audio_files %}
<audio controls>
<source src={{ url_for('static', filename=file) }} type="audio/mpeg">
</audio>
{% endfor %}
<p>Click on play icon to play</p>
</body>
</html>

Output:

output of audio files in flask app

Cannot Serve Static Files

Ended up being I set the UPLOAD_FOLDER directory to

UPLOAD_FOLDER = '/profile_pictures'

Where it should've been

UPLOAD_FOLDER = 'profile_pictures'

Thanks for the answer @ Alexander : )

Flask app can't load CSS file from static folder

I've figured out the solution. I just needed to add the following to my __init__.py file:

package_dir = os.path.dirname(
os.path.abspath(__file__)
)
static = os.path.join(
package_dir, "static"
)
app.static_folder=static


Related Topics



Leave a reply



Submit