Milteven
Milteven

Reputation: 31

Serving HTML from Flask, can't find image?

I'm creating a Flask app frontend, my index.html can't find the images and files referenced in the code.

I've tried moving to the same folder but with no success.

Server:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return open('html/index.html').read()

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

HTML lines:

<img src="shards-logo-white.svg" alt="Example Navbar 1" class="mr-2" height="30">

<script src="js/shards.min.js"></script>
<script src="js/demo.min.js"></script>

Server debug output:

127.0.0.1 - - [30/Jan/2019 12:19:28] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Jan/2019 12:19:29] "GET /shards-logo-white.svg HTTP/1.1" 404 -
127.0.0.1 - - [30/Jan/2019 12:19:29] "GET /html/js/shards.min.js HTTP/1.1" 404 -
127.0.0.1 - - [30/Jan/2019 12:19:29] "GET /html/js/demo.min.js HTTP/1.1" 404 -

Upvotes: 0

Views: 1710

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1124768

No, Flask does not serve arbitrary files from the filesystem. Move any static files like those into the static subdirectory, then reference them via that path; you can use the full path of nested directoriess within the static path:

<img src="/static/shards-logo-white.svg" alt="Example Navbar 1" class="mr-2" height="30">

<script src="/static/html/js/shards.min.js"></script>
<script src="/static/html/js/demo.min.js"></script>

You could serve your HTML rendered from a template, at which point you can use {{ url_for('static', filename="shards-logo-white.svg") }} and {{ url_for('static', filename="html/js/shards.min.js") }}, etc. to have Flask generate valid URLs for those paths.

See the Static Files section of the quickstart, as well as the Static Files chapter of the tutorial.

If you are not using a template then may as well serve your html/index.html file as a static file too. As it stands you are not rendering it as a template nor are you making any other runtime changes to the file contents.

Even if you do sometimes need to conditionally serve a file from disk, rather than read it all into memory I'd use the flask.send_file() function to make sure the file is served efficiently and with a reasonable content-type header:

from flask import send_file

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

Relative paths are resolved against Flask.root_path. You probably also want to consider flask.send_from_directory(), which will ensure that user-input for a filename can't be abused to serve arbitrary files outside of a designated directory.

Upvotes: 1

Related Questions