Python flask upload file using ajax request.files empty - python

I am trying to upload an image about 1.62MB to an end point written using flask. the request.files object is always empty. I've checked the following questions but no luck:
Flask request.files is empty
https://github.com/requests/requests/issues/2505
How to upload a file using an ajax call in flask
here is my server:
from flask import Flask, request, jsonify, render_template
import sys
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = r"C:\Temp"
app.debug = True
#app.route("/demo-upload", methods=["GET", "POST"])
def ProcessImage():
if request.method == "POST":
print(request.files)
try:
if 'file' in request.files:
with open("test-upload.png", "wb") as iFile:
print(request['file'])
iFile.write(request.files['file'])
except Exception as e:
print(e)
return jsonify("Ok")
#app.route("/", methods=["GET"])
def DemoIndexPage():
return render_template("index.html")
if __name__ == "__main__":
app.run()
my client:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<script src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<title>Demo</title>
</head>
<body>
<h1 style="text-align: center">Status Demo</h1>
<span>upload image to process.</span><br/>
<form id="FileForm" name="file" enctype="multipart/form-data">
<input type="file" name="file" id="File" />
<input type="button" name="submit" value="submit" onclick="ProcessImage()" />
</form>
<p id="status" hidden>Success!</p>
<script>
function ProcessImage()
{
var form_data = new FormData($('#File')[0]);
console.log(form_data)
$.ajax({
type: 'POST',
url: '/demo-upload',
data: form_data,
contentType: false,
cache: false,
processData: false,
async: false,
success: function (data) {
console.log('Success!');
$("#status").show();
},
});
}
</script>
</body>
</html>
everything looks clean to me and I do not know where I am going wrong. the files attribute in the request object is always empty. I also tried with postman using post request with form-data key = file and value = uploaded a file, and a header content-type = "multipart/form-data". any help is appreciated thanks a lot!

I made a few changes and make it work:
First, change which html element you read the data from in javascript part:
var formDataRaw = $('#FileForm')[0];
var form_data = new FormData(formDataRaw);
Second, I tried to obtain the uploaded image as follows: (#cross_origin() is only required if you try to upload to your localhost)
#app.route("/demo-upload", methods=["GET", "POST"])
#cross_origin()
def ProcessImage():
if request.method == "POST":
print(request.files)
try:
if 'file' in request.files:
imageFile = request.files['file']
savePath = "/somewhere/somewhere/something.png"
imageFile.save(savePath)
except Exception as e:
print(e)
return jsonify("Ok")

Related

How to upload a (csv) file with an AJAX call and Flask

I'm updating on updating an older project that uses AJAX and have decided to use Flask to do so. For the particular page I'm currently working on, I need to be able to upload a CSV and read the data in the file (no need to save it). I have several other pages that work using AJAX, but they return form data back to Flask (e.g. what semester it is, what year it is, etc). Ideally, I'd like to be able to upload the CSV and read the form data (the variables I have called formData and myFormData below).
I have found this post and based my MWE on it, but when I look at request.files, I get an empty dictionary. Here is the code I have:
run.py:
import os
from app import app
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host='0.0.0.0', port=port, debug=True)
__init__.py:
from flask import Flask, session
import flask_excel as excel
from fileUpload import fileUpload_bp
def create_app():
app = Flask(__name__, template_folder="templates")
app.secret_key = 'flask-ajax file upload test'
app.register_blueprint(fileUpload_bp)
excel.init_excel(app)
return app
app = create_app()
file_upload.py:
from flask import Flask, render_template, request, Blueprint
fileUpload_bp=Blueprint('fileUpload',__name__)
#fileUpload_bp.route('/fileUpload',methods=['GET','POST'])
def fileUpload():
if request.method=="POST":
print(request.files)
return render_template("fileUpload.html")
fileUpload.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>file upload test</title>
<script type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript"
src="static/scripts/fileUpload.js"></script>
</head>
<body>
<form action="javascript:fileUpload()" method="POST" enctype="multipart/form-data">
<input type="file" id="file_upload_data"><br>
<input type="text" id="form_data" value="sample data">
<button type="submit">Upload</button>
</form>
</body>
</html>
fileUpload.js:
function fileUpload()
{
var formData=new FormData($("file_upload_data")[0]);
var myFormData={form_data: $("#form_data").val()};
$.ajax({
type: 'post',
dataType: 'html',
url: 'fileUpload',
async: false,
data: formData,
contentType: false,
cache: false,
processData: false,
success: function (data){
console.log('Success');
},
error: function(response, status, xml) {
console.log('failure');
}
});
}
A little additional info: This is part of a larger project which is why I'm using Blueprints and flask_excel. I've seen folks recommend using something other than AJAX, but I'm trying to make the pages run with python3 by using Flask without rewriting everything that's already there.
So that the form can be serialized, it is necessary for the input fields to have a name attribute.
I'm using the form's submit event in the following minimal example. The event listener is registered when the document is fully loaded. When the form is submitted, the form data is serialized and sent via ajax.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Upload</title>
</head>
<body>
<form name="upload-form" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">Upload</button>
</form>
<script type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// Register the listener for submit events.
$('form[name="upload-form"]').submit(function(evt) {
// Prevent the form from default behavior.
evt.preventDefault();
// Serialize the form data. The entire form is passed as a parameter.
const formData = new FormData($(this)[0]);
// Send the data via ajax.
$.ajax({
type: 'post',
url: '/upload',
data: formData,
contentType: false,
cache: false,
processData: false,
}).done(function(data) {
console.log('success');
}).fail(function(xhr, status, error) {
console.error('error');
});
});
});
</script>
</body>
</html>
The server-side code remains essentially the same. However, I advise you, for reasons of cleanliness, to separate endpoints for ajax requests from those that return html.
from flask import Flask
from flask import make_response, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['POST'])
def upload():
print(request.files)
return make_response('', 200)

BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand. KeyError: 'search'

App.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="." method="post">
Search: <input type="text" name="search">
<input type="submit" value="Show">
</form>
</body>
</html>
main.py
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/CmpPr')
def cmpP():
return render_template('CmpPr.html')
#app.route('/CmpSpes')
def cmpS():
return render_template('CmpSpes.html')
#app.route('/App', methods=['POST', 'GET'])
def App():
search = request.form['search']
return render_template('output.html', n=search)
#app.route('/Gro')
def Gro():
return render_template('Gro.html')
if __name__ == '__main__':
app.run(debug=True)
I have created multiple html pages
I want to print the message, request from TextBox(above code) and print to another html page
I tried using request.form.get('search') but its returning null
And if I use request.form.get('search', FALSE or TRUE) it returns FALSE or TRUE
I have also used if else loop to specify GET and POST method, still it shows the same error
Can anyone please help me on this
Thank You
Firstly, your form action should point to the view that handle the form data (i.e. /App):
<form action="/App" method="post">
Secondly, you should only obtain the form data when the request's method is POST, since you have set method="post" in the template. Also, you will need to render the App.html that contains the form when request method is GET:
#app.route('/App', methods=['POST', 'GET'])
def App():
if request.method == 'POST': # get form data when method is POST
search = request.form['search']
return render_template('output.html', n=search)
return render_template('App.html') # when the method is GET, it will render App.html
P.S. The error you got is explained clearly that there isn't a key called search in the form data.

How to create a web push notification using Flask

I'm trying to implement a web push notification in my project. Using some tutorials I got create an alert on index page when I post a message. But it's far from being what I want.
index.html
<html>
<head>
<title>Test Page</title>
</head>
<body>
<h1>Testing...</h1>
</body>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<script type="text/javascript">
var source = new EventSource('/stream');
source.onmessage = function (event) {
alert(event.data);
};
</script>
</html>
post.html
<html>
<head>
<title>Posting a Message</title>
</head>
<body>
<form action="{{url_for('post')}}" method='post'>
Message: <input type="text" name="message" size='50'> <input type="submit" value="Launch!">
</form>
</body>
</html>
app.py
#!/usr/bin/env python
from flask import Flask, render_template, request, session, Response
from redis import Redis
import datetime
app = Flask(__name__)
app.secret_key = 'asdf'
red = Redis(host='localhost', port=6379, db=0)
def event_stream():
pubsub = red.pubsub()
pubsub.subscribe('notification')
for message in pubsub.listen():
print message
yield 'data: %s\n\n' % message['data']
#app.route('/post', methods=['POST','GET'])
def post():
if request.method=="POST":
message = request.form['message']
now = datetime.datetime.now().replace(microsecond=0).time()
red.publish('notification', u'[%s] %s: %s' % (now.isoformat(), 'Aviso', message))
return render_template('post.html')
#app.route('/stream')
def stream():
return Response(event_stream(),
mimetype="text/event-stream")
#app.route('/')
def index():
return render_template('index.html')
if __name__=="__main__":
app.run(host='0.0.0.0', port=8001, debug=True,threaded=True)
Well, I would like to implement a subscribe system, I think that is how is called. The user allow to receive notification from the website and when he clicks on the "news" it opens a new page with the detailed content.
The index page does not need to be opened for receving the message.

simple flask app server passing data with ajax and jquery

I've spent all day tinkering with this app trying to get some simple information passed to the back end of the application. I am using a simple flask app and trying to send data from a search query to the back end using ajax. However, I have been completely unsuccessful. Any help would be greatly appreciated.
Below is app.py
from scraper import scrape
from flask import Flask, render_template, jsonify, make_response, request
import json
app = Flask(__name__)
#app.route("/")
def index():
entries = json.dumps(scrape("video games"))
return render_template('index.html', entries= entries)
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data():
if request.method == "GET":
#data = request.form("blah")
#print("blah")
search = request.json
#new_search = json.dumps(scrape(data))
return search
return render_template('index.html')
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5000)
and index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask app</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div class="topnav">
<a class="active" href="#home">Home</a>
About
Contact
<form name = "textbox" id = "textbox">
<input id ="textbox" name="textbox" type="text" placeholder="Search..">
<button type="submit">submit</button>
</form>
</div>
<p>you searched: {{search}} </p>
<div id="div1">
<p id="p1"></p>
<p id="p2"></p>
</div>
<script>
var value = $('.textbox').val();
//alert(value);
$.ajax({
type: 'POST',
url: "/parse_data",
data: JSON.stringify(value)
contentType: 'application/json',
success: function(data){
alert("success")
}
});
var jsonz = {{ entries|tojson }};
var s = JSON.parse(jsonz);
var i;
for (i = 0; i < s.length; i++) {
var para = document.createElement("p");
var node = document.createTextNode(s[i].product_name + "\n" + s[i].product_link);
para.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(para);
}
//document.getElementById("user").innerHTML =
//obj;
//"Name: " + obj.product_name + "<br>" +
//"Location: " + obj.product_link;
</script>
</body>
</html>
Your code snippet has a few issues, mostly:
Your AJAX request is not bind to the button click event, so clicking the button does nothing.
You have two html elements with the same id textbox, id are supposed to be unique.
To get an html element by id use "#textbox"
On the server side (Flask):
Use the function get_json() of the request
To process the POST request you need to check for POST not GET
Try wrapping your POST request like this:
$("button").click(function (e) {
e.preventDefault();
var value = $("#textbox").val();
alert(value);
$.ajax({
type: "POST",
url: "parse_data",
data: JSON.stringify({ "text" : value } ),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
alert(JSON.stringify(data));
}
});
});
Also remove the duplicate ids textbox, change the id of the form to something like textbox-form, finally change your parse_data function to something like this:
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data():
if request.method == 'POST':
search = request.get_json()
return jsonify(search)
return render_template('index.html')

Get input filename and extension without actual uploading witn Flask

I use Flask+Python to locate relevant files for further processing. Currently I'm only able to upload files to a specified directory with the following:
Backend:
from flask import Flask, render_template, request
from werkzeug import secure_filename
app = Flask(__name__)
#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(secure_filename(f.filename))
return 'file uploaded successfully'
if __name__ == '__main__':
app.run(debug = True)
Frontend:
<html>
<body>
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<input type = "file" name = "file" />
<input type = "submit"/>
</form>
</body>
</html>
But I have several problems and questions with this solution:
I don't want actually touch (move/upload) any file, I only need the
filenames of the selected files. How to discard the actual uploading
and get the filenames as a list?
Is there any way to select a directory (and not specific files) for batch processing?
You can do it with Javascript ;)
Change your server side code to this:
from flask import Flask, render_template, request
from werkzeug import secure_filename
app = Flask(__name__)
#app.route('/upload')
def upload_file():
return render_template('upload.html')
#app.route('/uploader', methods = ['GET', 'POST'])
def uploader_file():
print(request)
if request.method == 'POST':
f = request.form['filename']
return f
if __name__ == '__main__':
app.run(debug = True)
And change your upload.html like this:
<html>
<body>
<script type="text/javascript">
function readURL(){
var fullPath = document.getElementById('upload').value;
if (fullPath) {
var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/'));
var filename = fullPath.substring(startIndex);
if (filename.indexOf('\\') === 0 || filename.indexOf('/') === 0) {
filename = filename.substring(1);
}
document.getElementById("filename").value = filename;
}
}
</script>
<input id="upload" type ="file" name = "file" onchange="readURL();" />
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<input id="filename" type="hidden" name="filename" />
<input type = "submit"/>
</form>
</body>
</html>
according to #RaminNietzsche's answer, I make a some changes.
frontend: input with attribute webkitdirectory enable web browser(Chrome, Firefox works, Safari doesn't) upload directory
<input id="directory" type='file' onchange="readFiles()" webkitdirectory>
<form action="{{ url_for('upload') }}" method="POST" enctype="multipart/form-data">
<input id="filenames" type="hidden" name="filenames"/>
<input type="submit"/>
</form>
<script type="text/javascript">
function readFiles() {
var directory = document.getElementById('directory').files;
var filenames = [];
for (var i = 0; i < directory.length; i++) {
filenames.push(directory[i].name);
}
document.getElementById("filenames").value = filenames;
}
</script>
backend:
#app.route("/upload", methods=["POST"])
def upload():
filenames = request.form.get('filenames', '').split(',')
# handle filenames here
return 'file uploaded successfully'

Categories