AJAX File(s) Upload using Python Flask and jQuery

File(s) Upload

I will create an example here and I will show you how to upload single file or multiple files using AJAX and jQuery along with Python Flask technologies. You can either upload single file using browse button or multiple files using browse button by holding CTRL key(in Windows OS) from keyboard while selecting multiple files.

The file or files is/are uploaded into a folder called uploads. Make sure this uploads folder exists in the system.

I will also show success message upon file(s) successfully get uploaded or error message upon file(s) get failed to upload.

The benefit of using AJAX technique is your entire page will not get refreshed on success or error and instead you will see a particular area on the page gets updated.

Related Posts:


Python 3.7.4/3.11.5, Flask 1.1.1/2.3.3

Project Directory

Create a project root directory called python-flask-ajax-files-upload as per your chosen location.

I may not mention the project’s root directory name in the subsequent sections, but I will assume that I am creating files with respect to the project’s root directory.

Configuring Application

I will configure application through flask framework. I will also define our file upload location and maximum size of all files a user can upload.

You should not allow user to upload unlimited size of file due to security reasons or to avoid exhausting server space.

Create a file called app.py with the below code. Make sure the uploads folder exists in the drive C otherwise you will get error (uploads folder/directory does not exist) during file upload.

from flask import Flask

UPLOAD_FOLDER = 'C:/uploads'

app = Flask(__name__)
app.secret_key = "secret key"
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

Template View File

I am using template file file-upload.html under templates directory to render the file upload page.

I will include jQuery library into <head/> section of the HTML page. As the example AJAX multiple files upload using Python Flask jQuery suggests and without jQuery or JavaScript technology AJAX technique will not work, jQuery library is required.

You may also use plain JavaScript to apply AJAX technique but jQuery provides many built-in functionalities ad easy to use.

I have put the upload field inside <body/> tag in HTML page.

Notice here how the name attribute is used.

I have also used multiple attribute to indicate that I will upload multiple files by holding CTRL key (in Windows OS) while selecting files.

I have added below jQuery, AJAX code immediate after the jQuery library inside <body> section of the HTML page.

When Upload button is clicked then I first create a new FormData object to store the file data.

I first retrieve the file counts and store each file into FormData object.

I then finally send the file data using python-flask-files-upload REST endpoint where server side code is written for uploading to the destination directory.

<!doctype html>
	<title>Python Flask File(s) Upload Example</title>
	<!--<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>-->
	<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
	<h2>Python Flask File(s) Upload - Select file(s) to upload</h2>
			<p id="msg"></p>
			<input type="file" id="multiFiles" name="files[]" multiple="multiple"/>
			<button id="upload">Upload</button>
	<script type="text/javascript">
		$(document).ready(function (e) {
			$('#upload').on('click', function () {
				var form_data = new FormData();
				var ins = document.getElementById('multiFiles').files.length;
				if(ins == 0) {
					$('#msg').html('<span style="color:red">Select at least one file</span>');
				for (var x = 0; x < ins; x++) {
					form_data.append("files[]", document.getElementById('multiFiles').files[x]);
					url: 'python-flask-files-upload', // point to server-side URL
					dataType: 'json', // what to expect back from server
					cache: false,
					contentType: false,
					processData: false,
					data: form_data,
					type: 'post',
					success: function (response) { // display success response
						$.each(response, function (key, data) {							
							if(key !== 'message') {
								$('#msg').append(key + ' -> ' + data + '<br/>');
							} else {
								$('#msg').append(data + '<br/>');
					error: function (response) {
						$('#msg').html(response.message); // display error response

View and REST Endpoint

I have defined root (/) endpoint that will just render the template view file with file upload option on the page.

I will create REST endpoint that will be used to upload single or multiple files into the server at a destination folder uploads.

I will show a successful message once file or files uploaded successfully or error messages depending on the server or client error.

If partial file(s) are uploaded then you will see both success and error messages.

For success message message is the key in the JSON response.

For error messages file name is the key in the JSON response.

I have defined a method allowed_file(filename) to allow user only upload allowed file types.

Only with successful operation I will send response status 201 otherwise I will send 400 bad request with error message.

Create a file main.py with the below source code.

import os
#import magic
import urllib.request
from app import app
from flask import Flask, flash, request, redirect, render_template, jsonify
from werkzeug.utils import secure_filename

ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

def allowed_file(filename):
	return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def upload_form():
	return render_template('file-upload.html')

@app.route('/python-flask-files-upload', methods=['POST'])
def upload_file():
	# check if the post request has the file part
	if 'files[]' not in request.files:
		resp = jsonify({'message' : 'No file part in the request'})
		resp.status_code = 400
		return resp
	files = request.files.getlist('files[]')
	errors = {}
	success = False
	for file in files:
		if file and allowed_file(file.filename):
			filename = secure_filename(file.filename)
			file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
			success = True
			errors[file.filename] = 'File type is not allowed'
	if success and errors:
		errors['message'] = 'File(s) successfully uploaded'
		resp = jsonify(errors)
		resp.status_code = 206
		return resp
	if success:
		resp = jsonify({'message' : 'Files successfully uploaded'})
		resp.status_code = 201
		return resp
		resp = jsonify(errors)
		resp.status_code = 400
		return resp

if __name__ == "__main__":

Deploying the Application

Now navigate to the project’s root directory using command line tool and execute the command python main.py, your server will be started on default port 5000.

If you want to change the port then you can change the line app.run() to app.run(port=5001), where 5001 is the new port.

Testing the File(s) Upload Application

Home Page

Hit the URL http://localhost:5000/ in the browser and you will see output as shown below in the image:

ajax files upload using python flask jquery

Validation Error – No File Selected

When you select no file and try to upload by clicking on the Upload button.

ajax files upload using python flask jquery

Partial Success – Allowed and non-allowed Files

Try to select multiple files (using Ctrl key) and click on Upload button.

So here ppt file is not allowed but pdf file is allowed to be uploaded into the server.

Therefore allowed file types are uploaded and non-allowed file types are not uploaded and displayed with error message including file name so you can be sure which file(s) were not allowed to upload.

ajax files upload using python flask jquery

Success – Allowed Type Files Only

Try to select multiple files (using Ctrl key) and click on Upload button.

In this case you will get only success message because all files are allowed in allowed types.

ajax files upload using python flask jquery

That’s all.

Source Code


Leave a Reply

Your email address will not be published. Required fields are marked *