Ajax File Upload with Dynamic Progress Bar

An AJAX file upload which will handle multiple file uploads with dynamic progress bars just like uploadify.js. Lets see how it looks so far and how we can handle ajax file uploads with dynamic progress with this script.

Ajax File Upload with Dynamic Progress Bar

Ok, Files we are going to need are index.php which will contain the form code, style.css will contain the styles of page, javascript.js will be holding the code to handle ajax file upload and process-upload.php will process the upload on server side.

The javascript file has a function "uploadFile(fd, count, file)" which is called for each uploaded file.

  • fd: Form data object.
  • count: counter of current file in loop.
  • file: The file itself
When form is submitted:
  1. It loops through all files and checks if file is an image. (For this post we will be processing only images).
  2. If file is an image it calls the function uploadFile providing it with parameters. "Form data", "Current file counter" and the "file" itself.
  3. If its not an image file then adds errors class to progress bar and does not upload the file.


index.php

<!DOCTYPE html>
<html>
<head>
<title>Ajax File Upload with Dynamic Progress Bar - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link rel="stylesheet" href="css/style.css" />

<script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="js/javascript.js"></script>
</head>
<body>
<div class="container">
<div class="my-4">
<form id="ajax-upload-form" class="ajax-upload-form" enctype="multipart/form-data">
<div class="row">
<div class="col-6">
<input type="file" class="file-input" name="ajax_file" multiple="multiple"/>
</div>
<div class="col-6 text-right">
<button type="submit" class="btn btn-blue">
<i class="fa fa-upload"></i>
Upload
</button>
</div>
</div>
</form>
</div>
<div class="progress-container"></div>
</div>
</body>
</html>

javascript.js

$(document).ready(function () {
$("#ajax-upload-form").submit(function (e) {
e.preventDefault(); // Prevent form from being submitted.

let fd = new FormData(); // Create new form data object to push files into
let files = $(".file-input")[0].files; // Getting all files from input field

// Loop through all files and append progress bar for each file
for (var i = 0; i < files.length; i++) {
let file = files[i];
let regex = /^(image)\//;
let progressElCount = $("#progress-" + i).length + i;

let progressEl ='<div class="progress" id="progress-' + progressElCount + '">' +
'<span class="abort">&times;</span>' +
'<div class="progress-title"></div>' +
'<div class="progress-bar"></div>' +
'</div>';

fd.append("ajax_file", file);

$(".progress-container").append(progressEl);// Append progress bar to container

// If file is an image then process upload otherwise add error class to progress element
if (file.type.match(regex)) {
// Function to upload single file
uploadFile(fd, progressElCount, file);
} else {
$("#progress-" + progressElCount).addClass("progress-error");
$("#progress-" + progressElCount).find(".progress-title")
.text(file.name + " is invalid");
$("#progress-" + progressElCount).find(".progress-bar").css({"width": "100%"});
}
// If all files have been uploaded reset the form
if (i === (files.length - 1))
this.reset();
}
});

// Remove progress bar with error class
$(document).on("click", ".progress-error > .abort", function () {
$(this).closest(".progress").fadeOut(3000, function () {
$(this).remove();
});
});
});

function uploadFile(fd, count, file) {
var ajax = $.ajax({
url: "<?=DEMO_URL?>/process-upload.php",// Server side script to process uploads
type: "POST",
data: fd,
processData: false, //Bypass jquery's form data processing
contentType: false, //Bypass jquery's content type to handle file upload
xhr: function () {
var xhr = $.ajaxSettings.xhr();
if (xhr.upload) {
var progressEl = $("#progress-" + count);

// Listen to upload progress and update progress bar
xhr.upload.addEventListener("progress", function (progress) {
var total = Math.round((progress.loaded / progress.total) * 100);
progressEl.find(".progress-bar").css({"width": total + "%"});
progressEl.find(".progress-title").text(file.name + " - " + total + "%");
}, false);

// Code to be executed if upload is aborted
xhr.addEventListener("abort", function () {
progressEl.fadeOut(3000, function () {
$(this).remove();
});
}, false);

// Update progress and remove it after upload has finished
xhr.addEventListener("loadend", function () {
progressEl.fadeOut(3000, function () {
$(this).remove();
});
}, false);

// Show an error on progress if an error has occured during upload
xhr.addEventListener("error", function () {
progressEl
.addClass("progress-error").find("status-count").text("Error");
}, false);

// Show timeout error on progress bar if upload request has timedout
xhr.addEventListener("timeout", function () {
progressEl
.addClass("progress-timedout").find("status-count").text("Timed Out");
}, false);
}
return xhr;
}
});

// Bind abort to current ajax request.
$(document).on("click", "#progress-" + count + " > .abort", function () {
ajax.abort();
});
}

style.css

*{
box-sizing: border-box;
}
html,body{
margin: 0px;
padding: 0px;
}
body{
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
line-height: 1.5;
}
.container{
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.my-4{
margin-top: 1rem;
margin-bottom: 1rem;
}
.row{
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.col-6{
width: 100%;
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
-webkit-box-flex: 0;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.text-right{
text-align: right;
}
.btn{
display: inline-block;
padding: 5px 10px;
cursor: pointer;
font: inherit;
}
.btn-blue{
background-color: #006699;
border: 1px solid #2b7cab;
color: #ffffff;
}
.progress{
background: #fff;
border: 1px solid #00a65a;
padding: .75rem 1.25rem;
margin-bottom: 1rem;
}
.progress > .abort{
font-size: 1.5em;
line-height: 1;
cursor: pointer;
font-weight: 700;
float: right;
}
.progress-error{
border: 1px solid #e65442;
color: #e65442;
}
.progress > .progress-bar{
background-color: #00a65a;
width: 0;
max-width: 100%;
height: 5px;
margin-top: 0.25rem;
transition: width ease .1s;
}
.progress-error > .progress-bar{
background-color: #e65442;
}

process-upload.php

<?php
// Code to process uploads
if($_FILES && !$_FILES['ajax_file']['error']){
$allowed_types = ['audio', 'image', 'video'];

$type = substr($_FILES['ajax_file']['type'],0,5);

// Check if uploaded file is image,audio or video then save file
if(in_array($type,$allowed_types)){
move_uploaded_file($_FILES['ajax_file']['tmp_name'], 'uploaded-files/'.$_FILES['ajax_file']['name']);
}
}