Flask-Uploads will not enforce allowing only .csv files - python

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)

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.

Related

Handling Flask-Reuploaded error when uploading a file with a disallowed filetype or no file

Hello,
I am trying to handle errors when uploading a file with a disallowed filetype or when submiting without selecting any file.
All I get is "flask_uploads.exceptions.UploadNotAllowed" and on the web page "Internal Server Error". I want to flash an error message but have no idea how to handle the error. I googled and I read the documentation but I couldn't find a way.
Everything works fine when selecting and submiting the right type of file/files(in this case IMAGES).
Thank you!
if request.method == 'POST':
if form.validate_on_submit() and 'artwork' in request.files:
art = form.artwork.data
this_artf = art_f+'/'+str(order_no_art)
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
for image in art:
images.save(image)
As you said, you have looked in the documentation...
I just enhanced the example shown at https://github.com/jugmac00/flask-reuploaded with handling the mentioned UploadNotAllowed exception.
Be sure to not forget to import the exception first!
...
from flask_uploads.exceptions import UploadNotAllowed
...
#app.route("/", methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'photo' in request.files:
try:
photos.save(request.files['photo'])
flash("Photo saved successfully.")
return render_template('upload.html')
except UploadNotAllowed:
flash("File type not allowed!")
return render_template('upload.html')
return render_template('upload.html')
This is a generic answer, but I am positive you can apply it to your case.
By the way, I saw you configured the app within the request handling:
if request.method == 'POST':
if form.validate_on_submit() and 'artwork' in request.files:
art = form.artwork.data
this_artf = art_f+'/'+str(order_no_art)
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
While this currently works, this is not the way it is supposed to do.
It is about these lines...
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
You have to move those lines outside the request context, e.g. at the top of your module or in your application factory.
Disclaimer: I am the maintainer of Flask-Reuploaded.
Handle flask_uploads.exceptions.UploadNotAllowed exception when invoking save method on instance of UploadSet.
from flask_uploads.exceptions import UploadNotAllowed
from flask import flash # make sure to configure secret key for your app
#....
error_files = []
for image in art:
try:
images.save(image)
except flask_uploads.exceptions.UploadNotAllowed as err:
error_files.append(image.filename)
continue
flash(error_files, 'error')
You could then, present the files in the rendered template by getting the flashed messages.

Error when trying to connect to flask via localhost

I can across a script on github that send a Post request using flask.
I am trying run the script to establish a connection on my local host. But I get the below response
The method is not allowed for the requested URL.
#app.route('/', methods = ['POST'])
def index():
if 'file' not in request.files:
return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit a empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
warped = transform_file(file)
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
if __name__ == '__main__':
app.run(host='127.0.0.1', threaded=True)
you are trying to have only a root i.e "/" in your route.
Can you try with a site, something like below:
#app.route('/', methods=['GET', 'POST'])
First of all it's because you do not have a GET method for the route: / address but only POST and that's the reason why Flask displays that error.
Definitely you should have a return statment at the end of function with either html code as #CoderRambo wrote in the comment or using render_template function pointing html file inside the templates folder.
Also if your going to use:
#app.route('/', methods=['GET', 'POST'])
you should add an if statment if request.method == 'POST': beforehand that partif 'file' not in request.files: - that would handle the post request and in else block have the return statment that will display the page onto which your going to enter data you'd like to post. As Flask's documention shows https://flask.palletsprojects.com/en/1.1.x/quickstart/:
Hope I helped.
Cheers

How do I get flask to send a file on form submit?

I'm creating a web application for an internship that send a docx document based from a template that contains data from a form. I'm using ajax, which allows me not to have to reload the page on submit and my back-end is in Flask from Python.
main.py
from flask import Flask, render_template, send_file, request
import generator
import os
app = Flask(__name__)
app.secret_key = os.urandom(24)
#app.route('/',methods=["GET,POST"])
def index():
if request.method == "POST":
template = "tplReceipt.docx"
form_data = request.form
document = generator.from_template(template, form_data)
document.seek(0)
return send_file(
document, mimetype='application/vnd.openxmlformats-'
'officedocument.wordprocessingml.document', as_attachment=True,
attachment_filename='invoice.docx')
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
generator.py
def from_template(template,form_data):
template = DocxTemplate(template)
context = get_context(form_data) # gets the context used to render the document
target_file = BytesIO()
template.render(context)
template.save(target_file)
return target_file
At debugging, everything is fine and my informations are filled, however my file isn't downloaded when I hit the submit button, why is that?

How do I request data (in form of image) to server in Flask?

How do I request data (in form of image) to server in Flask?
import os
from flask import Flask, request, redirect,url_for, Response
from flask import Markup, send_from_directory
from werkzeug.utils import secure_filename
UPLOAD_IMAGE = '/home/tarak/Pictures/Wallpapers/m31.jpg'
ALLOWED_EXTENSIONS = set(['jpg','png','txt','jpeg','gif'])
app = Flask(__name__)
#app.route('/image', methods=['POST'])
def image():
if request.method == 'POST':
f = request.files['UPLOAD_IMAGE']
request_
print("Image Received!!")
return "This is the homepage."
if __name__ == "__main__":
app.run(debug=True)
I am getting 404 Not Found Error on localhost:5000/image.
You get the error because, your route #app.route("/image", methods=['POST']) accepts only a post request, and when opening the URL in your browser it recieves a 'GET' request.
If you modify it to #app.route("/image", methods=['GET', 'POST']) or remove methods=.. you will see in your browser "This is the homepage"

flask: `#after_this_request` not working

I want to delete a file after the user downloaded a file which was created by the flask app.
For doing so I found this answer on SO which did not work as expected and raised an error telling that after_this_request is not defined.
Due to that I had a deeper look into Flask's documentation providing a sample snippet about how to use that method. So, I extended my code by defining a after_this_request function as shown in the sample snippet.
Executing the code resp. running the server works as expected. However, the file is not removed because #after_this_request is not called which is obvious since After request ... is not printed to Flask's output in the terminal:
#!/usr/bin/env python3
# coding: utf-8
import os
from operator import itemgetter
from flask import Flask, request, redirect, url_for, send_from_directory, g
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '.'
ALLOWED_EXTENSIONS = set(['csv', 'xlsx', 'xls'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
def after_this_request(func):
if not hasattr(g, 'call_after_request'):
g.call_after_request = []
g.call_after_request.append(func)
return func
#app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
#after_this_request
def remove_file(response):
print('After request ...')
os.remove(filepath)
return response
return send_from_directory('.', filename=filepath, as_attachment=True)
return '''
<!doctype html>
<title>Upload a file</title>
<h1>Uplaod new file</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
'''
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)
What do I miss here? How can I ensure calling the function following to the #after_this_request decorator in order to delete the file after it was downloaded by the user?
Note: Using Flask version 0.11.1
Just import after_this_request from flask, you don't need to modify after_request or create a hook.
from flask import after_this_request
#after_this_request
def remove_file(response):
print('After request ...')
os.remove(filepath)
return response
Make sure to import the decorator from flask.after_this_request. The decorator is new in Flask 0.9.
If you are using Flask 0.8 or older, then there is no specific after this request functionality. There is only a after every request hook, which is what the snippet coopts to handle per-request call-backs.
So unless you are using Flask 0.9 or newer you need to implement the documented hook yourself:
#app.after_request
def per_request_callbacks(response):
for func in getattr(g, 'call_after_request', ()):
response = func(response)
return response
So that hook is run after each and every request, and looks for a list of hooks to call in g.call_after_request. The after_this_request decorator registers a function there.

Categories

Resources