Skip to content
Advertisement

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.

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)
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement