7

I have 2 static directories in Flask.

static/

  • css/
  • js/

results/

  • 1/
    • index.html
    • details.json
  • 2/
    • index.html
    • details.json

I followed along a few other answers and glanced through the documentation for serving static files.

app = Flask(__name__)
app.config['RESULT_STATIC_PATH'] = "results/"

@app.route('/results/<path:file>')
def serve_results(file):
    # Haven't used the secure way to send files yet
    return send_from_directory(app.config['RESULT_STATIC_PATH'], file)

With the following code I can now request for files in the results directory.

I'm more familiar with PHP and Apache. Say if a directory has an index.php or index.html file, it is served automatically.

My requirement:-

  • Access any file in the results directory
  • If I send a request for localhost:3333/results/1 I should be served index.html

I can do the same with Flask by adding a route and checking if index.html exists withing the sub directory and then serving it. I found a few more similar pointers here

I currently use two routes to get the functionality. There certainly is a better way.

Why did I include details about the static directory?
It is highly possible that I may be doing something wrong. Please let me know. Thanks :)

Community
  • 1
  • 1
Abhirath Mahipal
  • 938
  • 1
  • 10
  • 21
  • 2
    Don't use Flask to serve your static files. Use Nginx or Apache. They're much better at it than Flask is and they both work the way you want. – dirn Sep 17 '16 at 12:14
  • 1
    It's a small testing application for internal use. I don't think speed/security matter here. It's always going to run on a LAN. Will remember this tip and use it when we build something client facing :) – Abhirath Mahipal Sep 17 '16 at 12:17
  • 1
    Yes, but you're probably getting to spend more time getting Flask to do what you want than you would setting up a tool that already does it. – dirn Sep 17 '16 at 16:32
  • 1
    Can't deny that. But in my case I'm almost done with everything in Flask (including this issue, was searching for a better way to get this functionality). I wish I knew it in the start. – Abhirath Mahipal Sep 18 '16 at 05:23
  • Does this answer your question? [How to serve static files in Flask](https://stackoverflow.com/questions/20646822/how-to-serve-static-files-in-flask) – ggorlen May 01 '23 at 18:06

3 Answers3

5

Try this:

@app.route('/results/<path:file>', defaults={'file': 'index.html'})
def serve_results(file):
    # Haven't used the secure way to send files yet
    return send_from_directory(app.config['RESULT_STATIC_PATH'], file)

This is how you pass a default value for that argument. However, I don't agree to this, if you want to serve static content just set a rule in your web server (apache in your case).

ipinak
  • 5,739
  • 3
  • 23
  • 41
  • Upvoted for the new pointer. I'm unable to get it to meet both my requirements. Tried different routes, I'm only able to meet one of my requirements. – Abhirath Mahipal Sep 17 '16 at 09:40
  • So you try `http://localhost:3333/results/onefile.txt` and it does not serve that file? If so, what does it respond with? – ipinak Sep 17 '16 at 09:44
  • If I use defaults I'll have to find the directory in the route and append it to `app.config['RESULT_STATIC_PATH']`. Am I right? – Abhirath Mahipal Sep 17 '16 at 09:50
  • It worked for me, I don't know why it does not work for you. Are you sure that the directory structure is correct? '1' is a directory right? – ipinak Sep 17 '16 at 10:12
  • Yes `1` is a directory. Does /results/anotherdir/onefile.txt opens onefile.txt and /results/anotherdir opens index.html? – Abhirath Mahipal Sep 17 '16 at 10:14
  • No it opens the index.thml of the parent. Here is the documentation of the function that you are using http://flask.pocoo.org/docs/0.11/api/#flask.send_from_directory – ipinak Sep 17 '16 at 10:20
  • The filename that you use is relative to the path you are in, which means that it serves the files of `app.config['RESULT_STATIC_PATH']`. This is done for security reasons. What you are trying to do is create a security whole to your system. Take a look at this https://www.owasp.org/index.php/Path_Traversal – ipinak Sep 17 '16 at 10:22
  • That it works for me too. I want it to open the index.html in it's directory. I'm using the two route approach now. – Abhirath Mahipal Sep 17 '16 at 10:43
1

I'm using the following single route solution, the "defaults" didn't work for me, as it stopped serving any other file.

Instead of raising the error when the path does not end with a slash, you can also generate a redirect to the path with a slash. Not having this slash but serving the index file with cause problems with relative paths.

@app.route('/<path:path>')
def staticHost(self, path):
    try:
        return flask.send_from_directory(app.config['RESULT_STATIC_PATH'], path)
    except werkzeug.exceptions.NotFound as e:
        if path.endswith("/"):
            return flask.send_from_directory(app.config['RESULT_STATIC_PATH'], path + "index.html")
        raise e
Daid Braam
  • 173
  • 4
-1

I couldn't find a solution which only used one route. I ended up using this.

@app.route('/results/<path:filename>')
def serve_static(filename):
    return send_from_directory("results/", filename)

@app.route('/results/<guid>')
def index(guid):
  return send_from_directory("results/" + guid, 'index.html')

Other suggestions

@ipinak suggested using defaults={'file': 'index.html'} as another parameter. That doesn't solve this problem as it results in some security issue which @ipinak has been kind enough to research about. defaults is a neat option which I'll try using in other places of my application as well.

@dirn suggested in the comments to use Apache/Nginx to serve static files. It would save time as I wouldn't have to implement stuff in Flask. Since static files don't have to passed to the the Python interpreter, we can serve them via another server with hardly any extra work.

Abhirath Mahipal
  • 938
  • 1
  • 10
  • 21