36

I followed the instructions from How to serve static files in Flask, but still couldn't get it working.

Here's my project structure:

Project_path  
 |
 +--app
 |  |
 |  +--main.py
 +--static
    |
    +--js
       |
       +--jquery-1.11.2.min.js

Here's main.py:

@app.route('/js/<path:path>')
def serve_static(path):
    root_dir = os.path.dirname(os.getcwd())
    print(os.path.join(root_dir, 'static', 'js', path))
    return app.send_static_file(os.path.join(root_dir, 'static', 'js', path))

Here's index.html:

  ...
  <script type="text/javascript" src="/js/jquery-1.11.2.min.js"></script>
  ...

And when I visit /, I could see the correct path of javascript file printed on the screen
which is Project_path/static/js/jquery-1.11.2.min.js.

But still, I got

127.0.0.1 - - [22/Dec/2014 11:26:30] "GET /js/jquery-1.11.2.min.js HTTP/1.1" 404 -

Any help is appreciated.

EDIT
After stepping through the send_static_file method, I find out what's going on. Basically, I shouldn't use abspath as argument, flask has a judgement in send_static_file:

if os.path.isabs(filename) or \
   filename == '..' or \
   filename.startswith('../'):
    raise NotFound()

And since the filename I passed into is a abspath, flask raise NotFound().
It seems that what it supposed to be passed in is a relative path to self.static_folder(self is <Flask 'main'>), which, in my project, is Project_name/app/static. However, I didn't set static_folder myself which means flask thinks the static folder should be there.

I'm still trying to figure out what to do.

Community
  • 1
  • 1
laike9m
  • 18,344
  • 20
  • 107
  • 140
  • Why are you going through all this? Flask will automatically serve static files. You don't need to write a view for it. – Burhan Khalid Dec 22 '14 at 07:22
  • 2
    @BurhanKhalid Not quite true. flask serves static files automatically if 1. set src="/static/js/xxx.js"; 2. static folder is in the same directly with `app.py`. – laike9m Dec 22 '14 at 07:38
  • for me this one worked : @app.route('/static/') def serve_static(filename): root_dir = os.path.dirname(os.getcwd()) return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename) – alone Jul 12 '16 at 20:50

5 Answers5

59

Finally got it working. use flask.send_from_directory

from flask import send_from_directory

@app.route('/js/<path:filename>')
def serve_static(filename):
    root_dir = os.path.dirname(os.getcwd())
    return send_from_directory(os.path.join(root_dir, 'static', 'js'), filename)

It is now clear to me that flask really hate people putting app.py or in my case main.py into a subdirectory. Use send_static_file only if your static folder is what flask thinks to be, i.e. a folder with name static in the same directory with app.py.

laike9m
  • 18,344
  • 20
  • 107
  • 140
  • 1
    you can replace root_dir with **'.'** - `return send_from_directory(os.path.join('.', 'static', 'js'), filename)` – Tom Lime Sep 07 '16 at 13:09
  • This answer is not completely true. For blueprints you can set another static folder and this static folder could be with any name. – makkasi Nov 25 '19 at 14:11
  • Dear future me: Careful when passing a _relative_ path as the first argument to `send_from_directory`. It will NOT be interpreted relative to the current working directory, if that is what you are after. – Martin Cejp Feb 23 '22 at 17:21
17

All you need to do is, pass the static_folder parameter to the initiator:

static_url_path – can be used to specify a different path for the static files on the web. Defaults to the name of the static_folder folder.

static_folder – the folder with static files that should be served at static_url_path. Defaults to the 'static' folder in the root path of the application.

app = Flask(__name__, static_folder=os.path.abspath('/foo/bar/zoo/'))

Now, flask will look for a directory named static in /foo/bar/zoo from where to serve static files. You only use send_from_directory if you are serving media files which may not be in the same location as static files.

csvoss
  • 117
  • 2
  • 14
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • 1
    What is the correct `static_url_path` for my project? And what should be the `src` attribute in html? – laike9m Dec 22 '14 at 07:54
  • Its the fully qualified path to the parent directory of `static`, and you don't need to make any changes in your src attribute, it will be exactly as in the [documentation](http://flask.pocoo.org/docs/0.10/quickstart/#static-files). – Burhan Khalid Dec 22 '14 at 07:55
  • So it's `static_url_path=Project_path/static/` and `src="/js/jquery-1.11.2.min.js"`, correct? – laike9m Dec 22 '14 at 08:03
  • No, remove the `static` part, and please read the documentation which explains it a lot better than I can in these comments. – Burhan Khalid Dec 22 '14 at 08:06
  • After many tests, I'm sure that if I want to put static files in seperate folders(css, js, ...), it's not possible to serve them only by modifying `static_url_path` and `static_folder`. Using `url_for` in jinja2 would work but that's another story. – laike9m Dec 24 '14 at 09:25
  • thanks, took a while to find this solution with `os.path.abspath()` – 8-Bit Borges Jan 14 '19 at 02:04
  • When do we use app.send_static_file over render_template? – variable Nov 08 '19 at 05:59
1

You forgot to add 'static' in the last os.path.join in the return clause.

Antoine Catton
  • 380
  • 3
  • 16
1

for me this one worked :

@app.route('/static/<path:filename>')
def serve_static(filename):
    root_dir = os.path.dirname(os.getcwd())
    return send_from_directory(os.path.join(root_dir, 'static', 'js'),   filename)       

beside adding this script into init

app._static_folder = os.path.abspath("static/")
app = Flask(__name__)

into __init__.py

Ice-Blaze
  • 897
  • 8
  • 18
alone
  • 169
  • 7
0

One possible cause of 404 error for pages you just added (even if programmed correctly), is if you have previous versions of the script (.py file) still running: make sure to close out and end (terminate) the process.

bob
  • 1