1

I have some problems with file serving with flask.

Firstly, my file tree:

processedMain
--processed1
--processed2
--...
--processedN

In each folder I have bunch of files that I represent it all in table where i put href link with path of each of files and pass it through route function with send_file return.

The problem is that the only way that I was able to make it work is to creating @app.route for every subfolder hardcoded, like this:

@app.route('/' + pathFits1[1] + '<id>', methods=['GET'])
def returnImg1(id):
    return send_file(pathFits1[1] + id, as_attachment=True, attachment_filename=id)

@app.route('/' + pathFits1[2] +'<id>', methods=['GET'])
def returnImg2(id):
    return send_file(pathFits1[2] + id, as_attachment=True, attachment_filename=id)

@app.route('/' + pathFits1[3] + '<id>', methods=['GET'])
def returnImg3(id):
    return send_file(pathFits1[3] + id, as_attachment=True, attachment_filename=id)

where pathFits1[i] is path to the subfolder and id is passed when user clicks on file in web table.

Problem is that I have a lot of subfolder, and its tiring to create @app.route for each. Is this possible to create just one route for every thing?

P.S. I'm quite new to flask, so if I forgot to tell something, please tell me I will fill it out.

Solution

Actually it was embarrassingly simple. As @Dan.D pointed, flask has convertors of captured variables, for me it was all I needed, as my id variable already contains full path to file. So now it's just this 3 lines which substitute a whole bunch or duplicated code:

@app.route('/<path:id>')
def returnImg(id):
    fileName = id.rsplit('/', 1)[-1]
    return send_file(id, as_attachment=True, attachment_filename=fileName)

Where id is full path to file, path: is converter to path and fileName is name of file that user will download without path in it (all after last slash)

Igor Kolesnikov
  • 133
  • 1
  • 8

2 Answers2

1

You can capture the path in the route expression. From the documentation:

@app.route('/path/<path:subpath>')
def show_subpath(subpath):
    # show the subpath after /path/
    return 'Subpath %s' % subpath

From http://flask.pocoo.org/docs/1.0/quickstart/

Dan D.
  • 73,243
  • 15
  • 104
  • 123
0

As far as I know, Flask doesn't support multiple variable sections in @app.route URLs. However, you could set the full path of the file as a variable section and then parse it to extract the sub-folder path and the file ID.

Here's a working example:

pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'

@app.route('/<path:fullpath>', methods=['GET'])
def returnImg(fullpath):
    global pathFits1
    print("--> GET request: ", fullpath)

    # parse the full path to extract the subpath and ID
    # subpath: get everything until the last slash
    subpath = fullpath.rsplit('/', 1)[:-1][0] + '/'
    # id: get string after the last slash
    id = fullpath.rsplit('/', 1)[-1]

    print("ID:", id, "\tsubpath:", subpath)

    # try to send the file if the subpath is valid
    if subpath in pathFits1:
        print("valid path, attempting to sending file")
        try:
            return send_file(subpath + id, as_attachment=True, attachment_filename=id)
        except Exception as e:
            print(e)
            return "file not found"
    return "invalid path"

However, a much better implementation would be to send the file ID and path as GET request parameters (eg. http://127.0.0.1:8000/returnimg?id=3&path=content/images1/) and retrieve them like this:

from flask import Flask, send_file, request

app = Flask(__name__)

pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'

@app.route('/returnimg', methods=['GET'])
def returnImg():
    global pathFits1
    id = request.args.get('id')
    path = request.args.get('path')

    print("ID:", id, "\tpath:", path)

    # try to send the file if the subpath is valid
    if path in pathFits1:
        print("valid path, attempting to sending file")
        try:
            return send_file(path + id, as_attachment=True, attachment_filename=id)
        except Exception as e:
            print(e)
            return "file not found"
    return "invalid path"
glhr
  • 4,439
  • 1
  • 15
  • 26