Skip to content

Instantly share code, notes, and snippets.

@uniphil
Created December 3, 2013 21:13
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uniphil/7777590 to your computer and use it in GitHub Desktop.
Save uniphil/7777590 to your computer and use it in GitHub Desktop.
send_from_directory

Say you have a package with this layout:

my_project/
    my_app/
        files/
            im_a_file.txt
        __init__.py
    run_my_app.py

...and you want to send files from files/. So in a view function in __init__.py or something, you do return send_from_directory('files', 'im_a_file.txt'). It doesn't work. Here's how it breaks:

  1. safe_join dumbly concatenates the directory and path (assuming they're safe), so the path returned is still relative, so you get filename == 'files/im_a_file.txt'.

  2. send_from_directory tests the safe filename with os.path.isfile, which works relative to the working directory, to see if the file exists so it can 404 if it doesn't. The test fails, because the working directory is project, not project/my_app. A passing filename would be my_app/files/im_a_file.txt'. Any file path in files/will just404`.

  3. Ok, so flask is weird, lets just hack our code to accommodate send_from_directory's use of the current working directory. We change our line to return send_from_directory('my_app/files', 'im_a_file.txt'). It still breaks:

3 a) from safe_join, we now get my_app/files/im_a_file.txt. my_app/files/im_a_file.txt passes the os.path.isfile test, so we don't 404 this time.

3 b) the new safe filename is passed to send_file. In send_file, the path is tested to see if it's an absolute path. It is not, so send_file makes it absolute by prepending current_app.root_path to it, which in our case of a package app is /path/to/my_project/my_app. So our filename is now /path/to/my_project/my_app/my_app/files/im_a_file.txt.

2 c) That path doesn't exist, so later when it's opened, it errors out with IOError.


To re-emphasize, this is an issue with with relative directories used with package apps. Absolute directories work, because current_app.root_path is never prepended. Module apps work because os.getcwd() == current_app.root_path for them.

@makmanalp
Copy link

I hit this exact same issue.

@Shaofanl
Copy link

Shaofanl commented Aug 8, 2015

Thx a lot. Sometime you use flask app as a module (like in flask-script), this issue pops up.

@sorawee
Copy link

sorawee commented Aug 28, 2015

Thx so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment