Ajax File Upload with Advance Progress Bar in Java

The file upload is a common functionality in most web applications. Nowadays the majority of people are focusing on user experience should be simple and smooth so when comes to files upload(single or multiple) the progress bar is giving the best user experience to any end-user. In this article, we will see how we can develop an advanced progress bar design to achieve the best user experience.

To develop the below example we have used HTML, JavaScript, jQuery, Bootstrap, Ajax for the front-end design, and Java Servlet on the back-end side. Let’s have a look at the below screenshot, this is what you can able to design at the end of this article.

File upload application running preview

Here we are going to focus more about developing the advanced progress bar that is the front-end UI part. We have explained about back-end (Server side) code in details in our another article File Upload in Java Servlet Example.

Javascript Ajax Progress Bar Implementation

Let’s understand the implementation of progress bar with sample points by referring the below sample example-

  1. In the below example we are taking some global variables(totalFileCount, fileUploadCount, fileSize) to store specific data.
  2. When the user will click on the Upload file button, the function startUploading() will be called. This is the initiation function of file upload. Here we are initializing the defined global variables.
  3. Before initiating the file uploads, we are calling the function prepareProgressBarUI() to design the progress bar UI.
  4. Here we are using XMLHttpRequest a Javascript class object to do the Ajax call. You can see in the function uploadFile().
  5. FormData a class object is used to attach the multipart file using append() method in order to upload it.
  6. progress, load, and error are the event which is fired when file upload will be happening. So we are going to bind these events with function onUploadProgress(), onUploadComplete(), and onUploadError() respectively.
  7. The function onUploadProgress() will be continuously called and update the progress bar. Here e.loaded will tell how many bytes have uploaded to the server, so based on that we are calculating the percentage of the progress bar.
  8. The function onUploadComplete() will be called when a file gets uploaded completely to the server. Here we are doing the next ajax call if more files yet to be upload otherwise updating 100% for the progress bar.
  9. The function onUploadError() will be called when an error occurs while uploading the files.

Sample Project Example

We are going to create a eclipse maven application. In this application the following files are required to create:

  • filesupload.html
  • FileUploadServlet.java
  • pom.xml

Eclipse Project Structure

advfileuploadjava eclipse project structure

filesupload.html

<!DOCTYPE PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>JavaCodePoint</title>
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<link rel="stylesheet"
	href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>
.action-icon{padding:5px;cursor:pointer;color:#fff}
.table{font-size:11px;}
.table>tbody>tr>td{padding: 2px 6px;vertical-align: middle;border:none;}
#main-container{padding: 0px 20px 40px; width: 50%;margin:auto;}
#upload-status-container{display:none;}
#upload-header{height:35px;width:100%;background-color: #323254;color: #fff;padding: 8px;border-top-left-radius: 10px;border-top-right-radius: 10px;}
#progress-bar-container{padding:20px;max-height:260px;overflow-y:auto;border:1px solid #323254;}
::-webkit-scrollbar {background-color: #fff; width: 8px; height: 8px;}
::-webkit-scrollbar-thumb {background-color: #C0C0C0; border-radius: 10px;}
</style>
</head>
<body>
	<div id="main-container">
		<h3 class="text-info">Advance progress-bar file upload using Ajax</h3>
		<hr>
		<div style="margin-bottom: 20px">
			<input type="file" id="files" multiple style="margin-bottom: 20px" />
			<button class="btn btn-primary" type="button" onclick="startUploading()" ><i class="fa fa-upload"></i> Upload file</button>
		</div>
		<div id="upload-status-container">
			<div id="upload-header">
				<span id="upload-header-text"></span>
				<i class="action-icon fa fa-window-minimize pull-right" onclick="showHide(this)" title="minimize"></i>
			</div>
			<div id="progress-bar-container">
				<table class="table">
					<tbody></tbody>
				</table>
			</div>
		</div>
	</div>
</body>
<script>
	/* Globle variables */
	var totalFileCount, fileUploadCount, fileSize;

	/* start uploading files */
	function startUploading() {
		var files = document.getElementById('files').files;
		if(files.length==0){
			alert("Please choose at least one file and try.");
			return;
		}
		fileUploadCount=0;
		prepareProgressBarUI(files);
		
		// upload through ajax call		
		uploadFile();
	}
	
	/* This method will be called to prepare progress-bar UI */
	function prepareProgressBarUI(files){
		totalFileCount = files.length;
		var $tbody=$("#progress-bar-container").find("tbody");
		$tbody.empty();
		$("#upload-header-text").html("1 of "+totalFileCount+" file(s) is uploading");
		for(var i=0;i<totalFileCount;i++){
			var fsize=parseFileSize(files[i].size);
			var fname=files[i].name;
			var bar='<tr id="progress-bar-'+i+'"><td style="width:75%"><div class="filename">'+fname+'</div>'
			+'<div class="progress"><div class="progress-bar progress-bar-striped active" style="width:0%"></div></div></td>'
			+'<td  style="width:25%"><span class="size-loaded"></span> '+fsize+' <span class="percent-loaded"></span></td></tr>';
			$tbody.append(bar);
		}
		$("#upload-status-container").show();
	}
	
	/* parse the file size in kb/mb/gb */
	function parseFileSize(size){
		var precision=1;
		var factor = Math.pow(10, precision);
		size = Math.round(size / 1024); //size in KB
		if(size < 1000){
			return size+" KB";
		}else{
			size = Number.parseFloat(size / 1024); //size in MB
			if(size < 1000){
				return (Math.round(size * factor) / factor) + " MB";
			}else{
				size = Number.parseFloat(size / 1024); //size in GB
				return (Math.round(size * factor) / factor) + " GB";
			}
		}
		return 0;
	}

	/* one by one file will be uploaded to the server by ajax call*/
	function uploadFile() {
		var file = document.getElementById('files').files[fileUploadCount];
		fileSize = file.size;
		var xhr = new XMLHttpRequest();
		var fd = new FormData();
		fd.append("multipartFile", file);
		xhr.upload.addEventListener("progress", onUploadProgress, false);
		xhr.addEventListener("load", onUploadComplete, false);
		xhr.addEventListener("error", onUploadError, false);
		xhr.open("POST", "UploadServlet");
		xhr.send(fd);
		
	}

	/* This function will continueously update the progress bar */
	function onUploadProgress(e) {
		if (e.lengthComputable) {
			var percentComplete = parseInt((e.loaded) * 100	/ fileSize);
			var pbar = $('#progress-bar-'+fileUploadCount);
			var bar=pbar.find(".progress-bar");
			var sLoaded=pbar.find(".size-loaded");
			var pLoaded=pbar.find(".percent-loaded");
			bar.css("width",percentComplete + '%');
			sLoaded.html(parseFileSize(e.loaded)+ " / ");
			pLoaded.html("("+percentComplete+ "%)");
		} else {
			alert('unable to compute');
		}
	}

	/* This function will call when upload is completed */
	function onUploadComplete(e, error) {
		var pbar = $('#progress-bar-'+fileUploadCount);
		if(error){
			pbar.find(".progress-bar").removeClass("active").addClass("progress-bar-danger");
		}else{
			pbar.find(".progress-bar").removeClass("active");
			pbar.find(".size-loaded").html('<i class="fa fa-check text-success"></i> ');
		}
		fileUploadCount++;
		if (fileUploadCount < totalFileCount) {
			//ajax call if more files are there 
			uploadFile();
			$("#upload-header-text").html((fileUploadCount+1)+" of "+totalFileCount+" file(s) is uploading");
		} else {
			$("#upload-header-text").html("File(s) uploaded successfully!");
		}
	}

	/* This function will call when an error come while uploading */
	function onUploadError(e) {
		console.error("Something went wrong!");
		onUploadComplete(e,true);
	}
	
	function showHide(ele){
		if($(ele).hasClass('fa-window-minimize')){
			$(ele).removeClass('fa-window-minimize').addClass('fa-window-restore').attr("title","restore");
			$("#progress-bar-container").slideUp();
		}else{
			$(ele).addClass('fa-window-minimize').removeClass('fa-window-restore').attr("title","minimize");
			$("#progress-bar-container").slideDown();
		}
	}
</script>
</html>

FileUploadServlet.java

package com.javacodepoint.fileupload;

import java.io.File;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
/**
 * 
 * @author javacodepoint
 *
 */
@WebServlet("/UploadServlet")
@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 2, // 2MB
		maxFileSize = 1024 * 1024 * 500, // 500MB
		maxRequestSize = 1024 * 1024 * 1024) // 1GB
public class FileUploadServlet extends HttpServlet {
	
	/**
     * Location to save uploaded files on server
     */
	private static final String UPLOAD_PATH = "C:/uploads";
	
	/**
     * Method to get file name from HTTP header content-disposition
     */
	private String getFileName(Part part) {
		String contentDisp = part.getHeader("content-disposition");
		String[] items = contentDisp.split(";");
		for (String s : items) {
			if (s.trim().startsWith("filename")) {
				return s.substring(s.indexOf("=") + 2, s.length() - 1);
			}
		}
		return null;
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			File uploadsPath = new File(UPLOAD_PATH);
			if (!uploadsPath.exists()) {
				//create upload folder if not exist.
				uploadsPath.mkdir();
			}
			for (Part part : request.getParts()) {
				String fileName = getFileName(part);
				if(fileName!=null) {
					part.write(UPLOAD_PATH + File.separator + fileName);
				}
			}
			System.out.println("File uploaded successfully!");
		} catch (Exception e) {
			System.err.println("Error while uploading files!");
			e.printStackTrace();
		}
	}

}

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javacodepoint</groupId>
	<artifactId>JavaFileUpload</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.8.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.3</version>
				<configuration>
					<warSourceDirectory>WebContent</warSourceDirectory>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Share with friends

Leave a Comment

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