pythonpython-3.xflaskflask-uploads

Flask-Uploads will not enforce allowing only .csv files


I'm using Flask, Flask-Bootstrap and Flask-Uploads with Python 3.7.1 to create a very simple application that accepts a csv file containing raw data.

The 'upload' page must allow only .csv files to be uploaded. I have tried to implement the answer given on this post.

Upload attempts with .csv work as expected, but other file types (eg .jpg) still appear to be accepted. Am I missing something obvious here?

'details.html' simply renders the filename on the page for now.

Python Code:

import os  
from flask import Flask, render_template, url_for, request
from flask_bootstrap import Bootstrap
from flask_uploads import UploadSet, configure_uploads

app = Flask(__name__)
Bootstrap(app)

# Upload files configuration
csv_file = UploadSet('files', ('csv'))
app.config['UPLOADED_FILES_DEST'] = 'static/uploadstorage'
configure_uploads(app, csv_file)

# index
@app.route('/')
def index():
    return render_template('index.html')

# if csv file, show the data in a table. if not csv file, reload index page
@app.route('/datauploads', methods=['GET', 'POST'])
def datauploads():

    if request.method == 'POST' and 'csv_data' in request.files:

        file = request.files['csv_data']
        filename = file.filename
        file.save(os.path.join('static/uploadstorage', filename))
        return render_template('details.html', filename=filename)

    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

Solution

  • You are ignoring the upload set when you accept files. You need to use the UploadSet.save() method for extension checking to kick in.

    You also need to pass in a sequence of extensions, currently you pass in a string, add a comma to make it a tuple:

    csv_file = UploadSet('files', ('csv',))
    

    and in your view use:

    @app.route('/datauploads', methods=['GET', 'POST'])
    def datauploads():
        if request.method == 'POST' and 'csv_data' in request.files:
            filename = csv_file.save(request.files['csv_data'])
            return render_template('details.html', filename=filename)
    
        return render_template('index.html')
    

    You probably want to catch the UploadNotAllowed exception, however, as you'd otherwise get a 500 error:

    from flask_uploads import UploadSet, configure_uploads, UploadNotAllowed
    from flask import flash
    
    @app.route('/datauploads', methods=['GET', 'POST'])
    def datauploads():
        if request.method == 'POST' and 'csv_data' in request.files:
            try:
                filename = csv_file.save(request.files['csv_data'])
                return render_template('details.html', filename=filename)
            except UploadNotAllowed:
                flash('Only CSV files can be uploaded, please correct', 'error')
    
        return render_template('index.html')
    

    I used message flashing (which Flask-Bootstrap can support directly), but your index.html could also be altered to accept an error message.