Search code examples
pythonreactjsflaskfilepond

Uploading images to a Flask server using FilePond (React component)


I have a locally hosted Flask server and a small React application. I'm trying to use FilePond as an easy solution for image uploading. FilePond takes care of sending each image to the server.

So the problem I'm obviously having is with the backend code. I've set up my server like this, per Flask's docs

UPLOAD_FOLDER='/images'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


def allowed_file(filename):
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
print(request.files)
    if request.method == "POST":
        # check if the post request has the file part
        if 'file' not in request.files:
            print('No file part')
            return make_response("No File Part", 400)
        file = request.files["file"]
        # if user does not select file, browser also submit an empty part
        # without filename
        if file.filename == '':
            print('No selected file')
            return make_response("No Selected File", 400)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename) # filenames can be dangerous!
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return make_response("Success", 201)

However, when put a

 print(request.files)

statement at the beginning of the upload_file() function, it seems that this is the request the server is receiving:

ImmutableMultiDict([('images', <FileStorage: 'imageName.jpg' ('image/jpeg')>)])

and I have no idea how to deal with that. I don't know if 'images' is where 'file' is supposed to be. I'm just not really sure where to go from here or what to do with the data being received. I don't know what is, I've never seen that <> syntax being used.

Can anyone help?

Here's a pastebin for the whole server-side code

Thank you!


Solution

  • Here's a proof of concept app that lets you upload multiple images using a form:

    Note the enctype="multipart/form-data" attribute of the form, without this you cannot upload files.

    Also note the multiple attribute of the file input. This allows client to select multiple files. You need to use request.files.getlist() to obtain the list of all uploaded files.

    Once you generate the path to save the file, saving a werkzeug.FileStorage object is is just calling its .save(path_to_save) method.

    from flask import Flask, request, render_template_string, redirect, abort
    from werkzeug import secure_filename
    from pathlib import Path
    
    UPLOAD_DIR: Path = Path(__file__).parent / 'uploads'
    UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
    
    app = Flask(__name__)
    
    
    def is_valid_upload(upload) -> bool:
        # some validation logic
        return Path(upload.filename).suffix.lower() in ['.jpg', '.jpeg']
    
    
    @app.route('/', methods=['GET', 'POST'])
    def upload():
        html = '''
            <form action='/' method='POST' enctype="multipart/form-data">
                <input type="file" name='images' multiple>
                <button>Upload</button>
            </form>
        '''
    
        if request.method == 'GET':
            return html
    
        uploaded_files = request.files.getlist('images')
        if not uploaded_files or not uploaded_files[0].filename:
            return redirect('/')
    
        valid_uploads = list(filter(is_valid_upload, uploaded_files))
        if not valid_uploads:
            return 'invalid image(s)', 400
    
        for upload in valid_uploads:
            filename = secure_filename(upload.filename)
            save_path = str(UPLOAD_DIR / filename)
    
            upload.save(save_path)
    
        return 'uploaded'
    
    
    if __name__ == "__main__":
        app.run()