Bad Request(400) when upload file, Flask - python

I'm trying to do a file upload to my Flask backend
My Python code
#app.route('/new_upload/', methods=['GET', 'POST'])
#login_required
def upload_file():
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flash("File uploaded: Thanks!", "success")
return redirect(url_for('upload.html'))
return render_template('upload.html', filename=filename)
My HTML looks like this:
{% extends "layout.html" %}
{% from "macros.html" import render_field %}
{% block content %}
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
{% endblock %}
In home page when I clicked on upload file link browser show me
Bad Request
The browser (or proxy) sent a request that this server could not
understand.
For clearance Home page HTML and image are attached bellow
<div class="main">
<nav>
All
{% if current_user.is_authenticated %}
Following
Create New Post
Upload file
{% endif %}
</nav>
{% block content %}{% endblock %}
</div>
Home Page
After click
Please try help me, I am just learning

In this piece of code
return redirect(url_for('upload.html'))
You should change url_for('upload.html') to url_for('upload') or what is suppose to be the name of the function instead of the html template.
Also if you are about to use the same function "def upload_file()" for HTTP GET and HTTP POST requests, then you should specify the piece of code that would be executed on post and another piece that would be executed when only GET request is performed. Something like:
# Import request if you haven't.
from flask import request
#app.route('/new_upload', methods=['GET', 'POST'])
#login_required
def upload_file():
if request.method == 'POST':
# This will be executed on POST request.
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flash("File uploaded: Thanks!", "success")
return redirect(url_for('upload_file'))
# This will be executed on GET request.
return render_template('upload.html')
I haven't tested the above code, but this should be the approach if you use one function for GET and POST http request.
If you do not differentiate the upload functionality (on POST HTTP request) and rendering the template (on GET request) it would try to execute all the code on every request and would fall in loop where would return redirect(url_for('upload_file')) every time and would not get to the return render_template('upload.html') where is suppose to show you page (HTTP request with code 200 instead of code 400).
You can strictly follow this example:
http://flask.pocoo.org/docs/0.10/patterns/fileuploads/ to get the overal idea.
You can also have a look at the HTTP Methods: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods so you would have better overview what is POST and GET request.

Related

not working pass variable in render_template

i am using python with flask. i want to send variable to html. getting html output.
ı pass filename variable with render_template. but i dont show in output variable in html
this my python code
#app.route('/', methods=['POST'])
def upload_image():
#cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No image selected for uploading')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
print('upload_image filename: ' + filename)
#cursor.execute("INSERT INTO upload (title) VALUES (%s)", (filename,))
#conn.commit()
#os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename))
filenamea = 'result_'+str(uuid.uuid4())+'.png'
input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
output_path = os.path.join(app.config['UPLOAD_FOLDER'], filenamea)
#session = new_session()
input = Image.open(input_path)
output = remove(input)
output.save(output_path)
flash('Image successfully uploaded and displayed below')
return render_template('index.html', filename=filename)
else:
flash('Allowed image types are - png, jpg, jpeg, gif')
return redirect(request.url)
#app.route('/display/<filename>')
def display_image(filename):
#print('display_image filename: ' + filename)
return redirect(url_for('static', filename='uploads/' + filename), code=301)
this my html code:
<body>
{% if filename %}
<div>
<img src="{{url_for('display_image', filename=filename)}}">
</div>
{% endif %}
<div class="wrapper">
<header>Image Backgroud Cleaner</header>
<form action="#">
<input class="file-input" type="file" name="file" multiple hidden>
<i class="fas fa-cloud-upload-alt"></i>
<p>Browse File to Upload</p>
</form>
<section class="progress-area"></section>
<section class="uploaded-area"></section>
</div>
<script src="{{url_for('static', filename='script2.js')}}"></script>
</body>
output
The excerpts above are evidently an incomplete piece of a whole application. Ideally, queries should be reduced to a minimal reproducible example that can be simulated without depending on the rest of your application. This isn't said to be critical but rather to engender your patience and grace if contributors like me have had to make assumptions about your specific scenario to answer your question.
The most pivotal assumption I've made is that the included html text is the template named index.html. Incidentally, it is importing script2.js. Only you know what that script is doing (not to belabor the point) so please pardon if you've already addressed the following suggestions in that javascript file or elsewhere.
Neither of the routes posted above have a GET method that will return index.html. <form action="#"> suggests that you are using the upload_image route for both the GET and POST of index.html, so the absence of a corresponding route supporting the GET method is very likely one of the problems that you're facing.
There are at least two approaches you could try to add a corresponding route supporting the GET method that returns index.html:
Method A - Update upload_image route to support GET method
#app.route('/', methods=['GET','POST'])
def upload_image():
if request.method == 'POST':
# All the POST logic goes here
else:
# All the GET logic goes here
return render_template('index.html')
Method B - Create a corresponding new route
#app.route('/upload_page', methods=['GET'])
def upload_page():
return render_template('index.html')
A reason for preferring method B is that you can leverage the upload_image route elsewhere to upload files in your application in different templating contexts if you haven't constrained it to index.html. I've favored method B in my index.html example below. Note that I've changed <form action="#" to <form action="/" so that it routes explicitly to the base route on POST.
Not only do you need to add a route that supports the GET method and returns index.html, but you also need to add method="post" enctype="multipart/form-data" as attributes of the form in index.html. Otherwise it will always default as a GET request and the intended POST will fail.
The hidden attribute is preventing the file input from rendering. If you're not intentionally using it for an unstated purpose, remove it.
You also need a submit input or other interface of some sort in index.html to submit the upload form.
Depending on if and how you configured an upload folder path, you may be running into issues with the route #app.route('/display/<filename>'). You actually don't need this route at all if you leverage the default by adding a folder named static in your application root directory. You need only change the url_for argument from display_image to static for this to work.
Given the above adjustments, index.html now looks like this:
<body>
{% if filename %}
<div>
<img src="{{url_for('static', filename=filename)}}">
</div>
{% endif %}
<div class="wrapper">
<header>Image Backgroud Cleaner</header>
<form action="/" method="post" enctype=multipart/form-data>
<input class="file-input" type="file" name="file" multiple>
<input type="submit">
<i class="fas fa-cloud-upload-alt"></i>
<p>Browse File to Upload</p>
</form>
<section class="progress-area"></section>
<section class="uploaded-area"></section>
</div>
<script src="{{url_for('static', filename='script2.js')}}"></script>
</body>
With these adjustments, you now should be getting the output variable filename that your question stated you were not getting in index.html and the <img> preview of filename should be visible in the rendered page.

Python - flask and werkzeug - Keep giving "BadRequestKeyError: 400 Bad Request: KeyError: 'file'" [duplicate]

This question already has an answer here:
What is the cause of the Bad Request Error when submitting form in Flask application?
(1 answer)
Closed 4 years ago.
I'm trying for the first time to upload file with python, I tried using flask and werkzeug libraries, here is my code:
Here I'm creating the function to upload files:
#app.route('/upload')
def upload_file():
return render_template('load.html')
#app.route('/uploader', methods=['GET','POST'])
#login_required(must=[be_admin, have_approval])
def uploaderV():
if request.method == 'POST':
file = request.files['file']
if file:
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
return 'file uploaded'
return render_template('load.html')
Than this is my load.html page:
{% extends 'base.html' %}
{% block title %}Secret{% endblock %}
{% block page_body %}
<div class="row">
<form action="{{ url_for('uploaderV') }}" method="POST" enctype="multipart/form-data">
<p>
<input type='file' name='file[]' multiple=''>
<input type="submit" value="Upload">
</p>
</form>
</div>
{% endblock %}
Every time I try to upload a file the server gives me werkzeug.exceptions.BadRequestKeyError
BadRequestKeyError: 400 Bad Request: KeyError: 'file'
I tried in different ways, and now I really don't know what to do.
Your input name is file[] , not file. Try something like:
file = request.files['file[]']
Or just change your input name to file.

Passing more than just the resultant page out of a Flask POST

(Flask novice alert)
Given the following to upload and save a file in Flask:
#app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h4>File uploaded</h4>
<p><a href={{ url_for('members_page') }}>Back</a></p>
{% endblock %}
''')
elif not allowed_file(file.filename):
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h3>Please try again</h3>
<h4>File must be a .csv</h4>
<p><a href={{ url_for('upload_file') }}>Back</a></p>
{% endblock %}
''')
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h4>Upload CSV of Company/URL data</h2>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" />
</form>
{% endblock %}
''')
I wish to make filename available within another function:
#app.route('/scrape', methods=['GET', 'POST'])
#login_required # Use of #login_required decorator
def scrape():
parser = ConfigParser()
parser.read('config.ini')
keywords = parser.get('scrape', 'keywords').replace(' ', '').split(',')
jobs = scraper.scrape(os.path.join(app.config['UPLOAD_FOLDER'], filename), keywords)
The above is the desired intent, where filename is known by the scrape fucnction. Obviously that is not yet the case. With upload_file() already having a return value in the positive case (a confirmation page), how can I make filename available? UPLOAD_FOLDER will contain more than just the uploaded file, so I can't just join this path with whatever is in there.
Where this a non-Flask program, I would probably return locals() and then access the appropriate key, but I imagine that's not possible here if I want to maintain the serving up of the confirmation page.
You need to somehow connect two requests. If many users request the first one, a then someone requests a /scrape, how do you know which one is requesting, and which filename does he belong to?
You can use a session (a cookie session for example, see http://pythonhosted.org/Flask-Session/) to keep track of the uploaded file. Store the filename in the session, and the when the same user (with the same cookie) requests /scrape, you can retrieve the filename from the user session.
You can include the filename to use in the second request. This way, the user himself has to keep track of the files that he uploaded.
In either case, but especially in the latter, it's important to think about ownership of files: which user has access to which file on your system?
Pickle the filename in upload_file(), unpickle it in scrape().
PICKLED_CSV_FILENAME = 'pickled_csv_file_name'
def pickle_filename(filename, pickle_file):
with open(os.path.join(UPLOAD_FOLDER, pickle_file),'wb') as p:
pickle.dump(filename, p)
def load_pickled_filename(pickle_file):
return pickle.load(open(os.path.join(UPLOAD_FOLDER, pickle_file), 'rb'))
in upload_file():
pickle_filename(filename, PICKLED_CSV_FILENAME)
then in scrape():
jobs = scraper.scrape(os.path.join(app.config['UPLOAD_FOLDER'], load_pickled_filename(PICKLED_CSV_FILENAME)), keywords)
pickle_filename(filename, PICKLED_CSV_FILENAME)
Obviously not a sustainable solution in the case of many users/files, but it is a single user, single file scenario so it's acceptable.

Python Flask get item from HTML select form

Im having trouble getting anything from the shown HTML form
I always get "ValueError: View function did not return a response"
Can somebody help me out here please? I have tried every variation of request.get that I can find on the web. Also if I specify my form should use post it uses get anyway - anybody know why this is?
Im new to flask so forgive my ignorance!
Thanks in advance.
The python file (routes.py)
from flask import Flask, render_template, request
import os
app = Flask(__name__)
musicpath = os.listdir(r"C:\Users\Oscar\Music\iTunes\iTunes Media\Music")
lsize = str(len(musicpath))
looper = len(musicpath)
#app.route('/')
def home():
return render_template('home.html', lsize=20, looper=looper, musicpath=musicpath)
#app.route('/pop', methods=['POST', 'GET'])
def pop():
if request.method == "GET":
text = request.args.get('som')
return text
#Have tried every variation of request.get
#app.route('/about')
def about():
name = "Hello!"
return render_template('about.html', name=name)
if __name__ == '__main__':
app.run(debug=True)
The html file (home.html)
{% extends "layout.html" %}
{% block content %}
<div class="jumbo">
<h2>A Music app!<h2>
</div>
<div>
{% if lsize %}
<form action="/pop">
<select id="som" size="20">
{% for i in range(looper):%}
<option value="{{i}}">{{ musicpath[i] }}</option>
{% endfor %}
</select>
</form>
{% endif %}
</div>
Select,
{% endblock %}
The Problem is that your HTML form does not have a name.
request.args.get("som") needs an HTML form input with the name "som"
<select name="som" id="som" size="20">
Just change that line and add a name. The form is not interested in the id attribute.
You don't specified the method of the form, you have to do it! For example use this<form method="POST action"/pop">
Your form action is /pop. That means that if you submit the form it will do a POST request to the address /pop. Your code does only return a value for a GET request, therefore Flask complains you do not return anything. Write some code to process a POST request and return a text or rendered template.
BTW, in the code for GET you refer to request.args.get('som'); this gives you request arguments (i.e. in the URL), not from the form. som is in the form, so you cannot refer to it this way.

How can I preview an image to be uploaded in flask?

I am trying to preview an image to be uploaded. I am using an upload form shown here
class Upload_Form(Form):
date = DateField('Date on Certificate', validators =...)
image = FileField(validators=[DataRequired()])
All that is really important for this is the image FileField. Here is the associated html with the upload. It extends a base html
{% block content %}
<form action="" method="POST" name="upload" enctype="...">
<p>{{ form.image.label }}<br>{{ form.image }}</p>
...
{% endblock %}
I need to either use python flask to preview the image or find a way to pull the URL from the file in the FileField to display the image with JS. I have tried form.image.url but that just returns an empty string. I know how to do this in JavaScript but I am having trouble understanding how to get it to be stored in the database since I am using a Python flask framework and it needs to be passed in the FileField from the class Upload_Form. I'm just having a hard time translating this to python flask as opposed to JS. Here is where I extract the file in flask.
#app.route('/upload', methods=['GET', 'POST'])
def upload():
form = Upload_Form()
if request.method == "POST" and form.validate_on_submit():
if img and os.path.splitext(img.filename)[1] in ALLOWED_EXTENSIONS:
try:
div_id = Division.query.filter_by(category=form.category.data).first().id
except AttributeError:
div_id = None
c = Certificate(div_id=div_id,user_id=g.user.id, status=False)
db.session.add(c)
db.session.commit()
...
return render_template('upload.html', title='Upload successful', form=form)

Categories