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.
Advertisement
Answer
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)