Flask accepting file from HTML form - Bad Request - python

Edited to include the first answers input:
My HTML form looks like this:
<!DOCTYPE html>
<html>
<body>
<form action="http://localhost:5000/upload_candidates" method='POST' enctype="multipart/form-data">
<input type="file" name="csv_file" accept="*">
<input type="submit">
</form>
</body>
</html>
the Flask endpoint looks like this:
#app.route('/upload_candidates', methods=['POST'])
def upload_candidates():
print('this worked')
file = request.files['file']
print('did this work?')
x = file.read()
print(x)
if __name__ == "__main__":
app.run(threaded=True, debug=True)
I'm getting an error: The browser (or proxy) sent a request that this server could not understand.
In the terminal:
* Detected change in '..../hello.py', reloading
* Restarting with stat
* Debugger is active!
* Debugger pin code: 238-076-488
this worked
In the network console:
Request URL:http://localhost:5000/upload_candidates
Request Method:POST
Status Code:400 BAD REQUEST
Remote Address:127.0.0.1:5000
It seems like it doesn't like the file = request.files['file'] line.
What am I doing wrong?

Your <form> is missing an enctype attribute:
<form action="http://localhost:5000/upload_candidates"
method="POST"
enctype="multipart/form-data">
Also, it appears that you are referencing a request.files member by the wrong name. Try this:
file = request.files['csv_file']

Related

Using Python to upload document to Dropbox API with Flask

Attempting to upload a document through Dropbox's API through a Submit button on Flask application. The HTML loads on localhost, but whenever I upload the document and hit Sumbit, there is a 404 error and the document does not post to the Dropbox API. Any ideas on where I'm going wrong?
Python
from flask import Flask, render_template, request
import dropbox
# Function Definition
def uploader(token, file):
target = '/temp'
targetFile = target + 'test.docx'
connection = dropbox.Dropbox(token)
meta = connection.files_upload(file, targetFile, mode=dropbox.files.WriteMode("overwrite"))
# Flask App
app = Flask(__name__)
#app.route('/', methods=['POST', 'GET'])
def upload_document():
if request.method == "POST":
uploader(token, request.files['file'])
return render_template('index.html')
if __name__ == "__main__":
app.run()
HTML
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form method = "post" action = "/home" enctype = "multipart/form-data">
<p>
<input type="file" name="file" autocomplete="off" required>
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
</body>
</html>
It looks like the issue stemmed from the script not reading the file when being passed through the function via the Dropbox connection. When using this, add file.read() within the connection.
# Function Definition
def uploader(token, file):
target = '/temp'
targetFile = target + 'test.docx'
connection = dropbox.Dropbox(token)
meta = connection.files_upload(file.read(), targetFile, mode=dropbox.files.WriteMode("overwrite"))

Python Flask: getting URL when moving to production

I'm using Python from quite some time but I am totally newbie in Flask. Can you help me with two simple questions I have been strugling two days?
I have a simple Flask app that supposed to import a XLSX or CSV, parse it and create a zip file to download. While I am begginig work on the parse part, I got an error when uploading the file, and I found out that the app is not saving the file altough it works when running flask locally.
This is the code:
test2.py
``` from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import pandas as pd
app = Flask(__name__)
def work(arquivo):
df = pd.read_excel(arquivo)
return(str(df.shape))
#app.route('/')
def start():
return "acesse /upload"
#app.route('/upload')
def upload_file():
return render_template('upload.html')
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
if __name__ == '__main__':
app.run(debug = True)
```
And this is the upload.html file that I put on templates folder in production (the app runs on http://www.fabianocastello.com.br/test2/upload):
<html>
<body>
<form action = "http://www.fabianocastello.com.br/test2/uploaded" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
when locally, the upload.html file that works is this:
<html>
<body>
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
The error I got after uploading the file is this:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
My questions are these:
1. Why the production app is not saving the uploaded file in "inbox" folder?
2. Is there a way that I substitute the URL in upload.html file from a variable so to I do not have to manually change the file before upload?
Thank you all in advance.
This is what the url_for method is for. That will automatically fix "http://localhost:5000/uploader" for you.
However, <form action = "http://www.fabianocastello.com.br/test2/uploaded" ...> points at a bigger misunderstanding. It would be horrendous if you had to alter every route in your templates moving from development to production. Your Flask routes needn't point to the specific domain that you're running your app on; they need only point to the endpoint of the server running your app (which might be gunicorn, for example). The Mega Tuorial might be helpful here for deployment. There's also more info in the deployment docs.
With that out of the way, there's other issues that need to be dealt with:
You have two route functions with the same name - upload_file. Why? It doesn't matter that you decorated them with different URLs, you can't have two functions with the same name in the same namespace.
The second upload_file is set to accept both GET and POST requests, but you only handle the POST case. Sending a GET request to this route will error.
This fixes the form:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
This consolidates the two route functions into one:
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
else:
return render_template('upload.html')
return 'file uploaded successfully '+a is going to give a garbage result, if any, though. It's not going to render a template with the message, it's just going to be unstyled text. It looks like you want AJAX, which would look something like this:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data" id="upload_file_form">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
<div id="response_div"></div>
</body>
<script>
$("#upload_file_form").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(),
context: form,
success: function(resp) {
$("#response_div").html(resp);
}
});
});
</script>
</html>

Output data on same page after form submit

So I created a small flask program which would take a file , do some processing and returns a stream of data using yield.
I am using html form for file upload and submit. The form sends file to a python script and returns the output. The issue is that the output is presented onto a different page because of the form action attribute whereas I need the output on the same page. Probably inside a div tag.
index.html
<script>
if (!!window.EventSource) {
var source = new EventSource('/upload');
source.onmessage = function(e) {
console.log(e)
var byte = e.data;
var res = byte.split(/\s/);
console.log(res[0])
$("#morse").text(res[0].slice(1,));
}
}
</script>
<form action="/upload" method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div> // this is where is need my data
Python code
#app.route('/')
def index():
return render_template('index.html')
#app.route("/upload", methods=['GET', 'POST'])
def streambyte():
if request.method == 'POST':
f = request.files['file']
list_of_items = unAssign(f) # some file processing
def events():
for i in list_of_items:
yield "data: %s\n\n" % (i)
time.sleep(1) # an artificial delay
return Response(events(), content_type='text/event-stream')
This streams the data on http://localhost:5000/upload whereas I need it on http://localhost:5000.
I tried using redirect with Response but it failed saying TypeError: 'generator' object is not callable
You may not need JavaScript to do this...
Since you need the result on the 'index.html' page (i.e http://localhost:5000), you need to create two routes for the same index page.
The first route will load the fresh form (method attribute not set), while the second will reload the process form (method attribute is set to POST). Both routes will point to same index page.
Here below is how your code should look like:-
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask App - Output data on same page after form submit</title>
</head>
<body>
<form method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div>
<h3>{{ result }}</h3>
<h3>{{ file_path }}</h3>
<!-- this is where is need my data -->
</body>
</html>
Python code
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route("/", methods=['GET', 'POST'])
def streambyte():
# your file processing code is here...
f = request.files['file']
your_script_result = 'This variable holds the final data output'
# your file processing code is here...
return render_template('index.html', file_path = f, result = your_script_result)
if __name__ == '__main__':
app.run(debug=True)
Read more from this link: Send data from a textbox into Flask?

Flask SMS with twilio error

from flask import *
from twilio import twiml
from twilio.rest import TwilioRestClient
from flask import render_template
import os
#Pull in configuration from system environment variables
TWILIO_ACCOUNT_SID = os.environ.get('Axxxxxx')
TWILIO_AUTH_TOKEN = os.environ.get('Sxxxxxxxxx')
TWILIO_NUMBER = os.environ.get('xxxxxxx')
# create an authenticated client that can make requests to Twilio for your
# account.
#client = TwilioRestClient(account='Axxxxx', token'sxxxxxxxx')
#create a flask web app
app = Flask(__name__)
client = TwilioRestClient(account='Axxxxx', token='Sxxxxx')
#app.route('/')
def homepage():
return render_template('index.html')
#Handling a post request to send text messages.
#app.route('/message', methods=['POST', 'GET'])
def message():
# Send a text message to the number provided
if request.method == 'POST':
message = client.sms.messages.create(to=request.form['Phone_number'],
from_=TWILIO_NUMBER,
body=request.form['body'])
return render_template('message.html')
if __name__ == '__main__':
# Note that in production, you would want to disable debugging
app.run(debug=True)
I am using flask. When i input the number and the text message it gives me this error
Method Not Allowed
The method is not allowed for the requested URL.
You're posting to the wrong endpoint. Your form should look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Send an SMS</title>
</head>
<body>
<form action="/message" method="POST">
Cell phone number: <input name="phone_number" type="text" />
Text in here: <input name="body" type="text" />
<button type="submit">Send Text</button>
</form>
</script>
</body>
</html>
(Above, action was changed from / to /message.)
Note: if this is a template run through flask.render_template, you should change
<form action="/message" method="POST">
to
<form action="{{ url_for('message') }}" method="POST">
This is a more sustainable way to use urls in flask, and it will reduce your overhead if you ever need to change the value.

Flask: How to handle application/octet-stream

I want to make a multiple file-upload form.I use jQuery File Uploader. My server-side code:
#app.route("/new/photogallery",methods=["POST"])
def newPhotoGallery():
print request.files
I tried two things:
Submit form normally:
When i submit my form normally,it prints:
ImmutableMultiDict([('post_photo_gallery', FileStorage: u'' ('application/octet-stream'))])
Submit form using AJAX:
When i submit my form using AJAX,it prints:
ImmutableMultiDict([])
My first question is: Why is there a difference between AJAX request and normal request.
My second question is: How can i handle this application/octet-streamrequest in Flask/Python
My third question is: Is this a good way to use application/octet-stream ?
By the way i do not know much about application/octet-stream.Thank you very much.
Regardless of the the data encoding, you should be able to get the raw data with request.data.
In the case of application/octet-stream, you can just write request.data to a binary file.
An example handler for various data types:
from flask import json
#app.route('/messages', methods = ['POST'])
def api_message():
if request.headers['Content-Type'] == 'text/plain':
return "Text Message: " + request.data
elif request.headers['Content-Type'] == 'application/json':
return "JSON Message: " + json.dumps(request.json)
elif request.headers['Content-Type'] == 'application/octet-stream':
with open('/tmp/binary', 'wb') as f:
f.write(request.data)
f.close()
return "Binary message written!"
else:
return "415 Unsupported Media Type ;)"
The typical scenario of handling form data is already documented here.
I was unable to get a request working using application/octet-stream type posts, but have used multipart/form-data type forms in the past to upload images using flask.
I have extended what I have done in the past to support multiple upload files and this has worked leveraging werkzeug's FileStorage objects.
The key here is setting up a post based route that is looking for a request element from a form. This should allow you to POST to the route either via a standard form or an AJAX call.
Below is a simplified example that is using a form:
Template for the view:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>jQuery File Upload Example</title>
</head>
<body>
{% if err %}
<h4>{{ err }}</h4>
{% endif %}
<form action="/" method=POST enctype=multipart/form-data id="fileupload">
<input type="file" name="files" data-url="/" multiple>
<input type=submit value=Post>
</form>
{% if files %}
{% for file in files %}
<p>Uploaded: <b>{{ file }}</b> </p>
{% endfor %}
{% endif %}
</body>
</html>
Flask App
from flask import Flask, request, render_template
from werkzeug import secure_filename, FileStorage
import os
# Flask functions
app = Flask(__name__)
app.config.from_object(__name__)
DEBUG = True
# add this so that flask doesn't swallow error messages
app.config['PROPAGATE_EXCEPTIONS'] = True
#app.route('/', methods=['GET', 'POST'])
def uploader():
if request.method =='POST' and request.files.getlist('files'):
up_file_list = []
# Iterate the through a list of files from the form input field
for a_file in request.files.getlist('files'):
if a_file.filename:
# Validate that what we have been supplied with is infact a file
if not isinstance(a_file, FileStorage):
raise TypeError("storage must be a werkzeug.FileStorage")
# Sanitise the filename
a_file_name = secure_filename(a_file.filename)
# Build target
a_file_target = os.path.join('/tmp/', a_file_name)
# Save file
a_file.save(a_file_target)
up_file_list.append(a_file_name)
# Return template
if up_file_list:
return render_template('uploader.html', err=None, files=up_file_list)
else:
return render_template('uploader.html', err='No Files Uploaded', files=None)
else:
return render_template('uploader.html', err=None, files=None)
# application execution
if __name__ == '__main__':
app.run()

Categories

Resources