Upload a File with Jersey using AJAX and HTML5 progress bar
In this tutorial we will show you how to upload a file with Jersey Rest Service using AJAX and HTML5 progress bar. First we start by creating our jersey restful service. We’ve chosen to configure jersey using annotations, this means that we no longer need the web.xml servlet descriptor. Next we create a web page which’ll use JQuery to make AJAX requests to the server to asynchronously upload an file, image or video to the server. Finally we use the new HTML 5 progress bar to display the upload status to the user.
Project Structure
This is a default Maven 3 Project. Here is an overview how the project is structured.
src
|--main
| +--java
| +--com
| +--memorynotfound
| +--rs
| |--ApplicationConfig.java
| |--FileUploadRestService.java
| +--resources
| +--webapp
| +--resources
| +--css
| |--progressbar.css
| +--js
| +--lib
| |--jquery-2.1.4.min.js
| |--upload.js
| |--index.html
pom.xml
Dependencies
Add the following dependencies to the project.
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.webservice.rs.jersey1</groupId>
<artifactId>file-upload</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>JERSEY - ${project.artifactId}</name>
<url>https://memorynotfound.com</url>
<properties>
<jersey.version>2.20</jersey.version>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
</dependencies>
</project>
Jersey File Upload Rest Service
This rest service will handle the upload request from the client. The @FormDataParam
annotation in conjunction with the media type multipart/form-data should be used for submitting and consuming forms that contain files, non-ASCII data, and binary data.
The InputStream
class holds the uploaded file.
The FormDataContentDisposition
class is simply a representation of the Content-Disposition
header and contains meta information about the uploaded file.
package com.memorynotfound.rs;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
@Path("/upload")
public class FileUploadRestService {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
@FormDataParam("file") InputStream in,
@FormDataParam("file") FormDataContentDisposition info) throws IOException {
File upload = new File(info.getFileName());
if (upload.exists()){
String message = "file: " + upload.getName() + " already exists";
return Response.status(Status.CONFLICT).entity(message).build();
} else {
Files.copy(in, upload.toPath());
return Response.status(Status.OK).build();
}
}
}
Registering Jersey RestServices
We register the rest service using annotations. For more information how to configure jersey with annotations only.
package com.memorynotfound.rs;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
public class ApplicationConfig extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(MultiPartFeature.class);
return resources;
}
@Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("jersey.config.server.provider.packages", "com.memorynotfound.rs");
return properties;
}
}
Upload File using HTML5 and AJAX
This web page will upload a file via AJAX and display the progress in a HTML 5 progress bar.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="resources/css/progressbar.css">
<script src="resources/js/lib/jquery-2.1.4.min.js"></script>
<script src="resources/js/upload.js"></script>
</head>
<body>
<h1>Upload a File</h1>
<progress max=100></progress>
<span id="progress_percent">0</span>%
<p>Choose : <input type="file" name="upload_file" /></p>
<button id="upload_btn">Start Uploading</button>
</body>
</html>
AJAX File Upload
The upload()
method will upload the file to the server asynchrounous using AJAX. When the method is called we add a progress event listener to the upload which’ll update the progress bar.
$(function(){
$('#upload_btn').click(upload);
});
function upload(){
var file = $('input[name="upload_file"]').get(0).files[0];
var formData = new FormData();
formData.append('file', file);
$.ajax({
url: 'api/upload',
type: 'POST',
data: formData,
cache: false,
contentType: false,
processData: false,
success: function(){
alert('file upload complete');
},
error: function(response){
var error = "error";
if (response.status === 409){
error = response.responseText;
}
alert(error);
},
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) {
myXhr.upload.addEventListener('progress', progress, false);
} else {
console.log('Upload progress is not supported.');
}
return myXhr;
}
});
}
function progress(e) {
if (e.lengthComputable) {
$('#progress_percent').text(Math.floor((e.loaded * 100) / e.total));
$('progress').attr({value:e.loaded,max:e.total});
}
}
Demo
URL: http://localhost:8080/jersey-file-upload/
References
- Injecting with @FormDataParam
- FormDataParam JavaDoc
- FormDataContentDisposition JavaDoc
- Styling HTML5 progress with CSS
I have a similar project but somehow when I deploy it I get an error. Have you encountered anything similar? Severe: Exception while loading the app : java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: org.apache.catalina.LifecycleException: org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization. [[FATAL] No injection source found for a parameter of type public javax.ws.rs.core.Response rover7c.rest.SegmentController.postSegment(java.lang.String,java.lang.String,java.io.InputStream,org.glassfish.jersey.media.multipart.FormDataContentDisposition) at index 2.; source=’ResourceMethod{httpMethod=POST, consumedTypes=[multipart/form-data], producedTypes=[application/json], suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, invocable=Invocable{handler=ClassBasedMethodHandler{handlerClass=class rover7c.rest.SegmentController, handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@189cd22e]}, definitionMethod=public javax.ws.rs.core.Response rover7c.rest.SegmentController.postSegment(java.lang.String,java.lang.String,java.io.InputStream,org.glassfish.jersey.media.multipart.FormDataContentDisposition), parameters=[Parameter [type=class java.lang.String, source=userId, defaultValue=null], Parameter [type=class java.lang.String, source=deviceId, defaultValue=null], Parameter [type=class java.io.InputStream, source=file, defaultValue=null], Parameter [type=class org.glassfish.jersey.media.multipart.FormDataContentDisposition, source=file, defaultValue=null]], responseType=class javax.ws.rs.core.Response}, nameBindings=[]}’, [WARNING] The (sub)resource method postSegment… Read more »
Dear Mike,
Have you based your code entirely on this tutorial?
It sounds like you are missing a configuration in your jersey application.
You need to register the MultiPartFeature in your jersey application configuration.
See section ‘Registering Jersey RestServices’
Hope this helps!
Hi! I am using configuration in a slightly different way. I believe both are valid, do you think this is what’s causing the problem?
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>rover7c.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
Hi Mike,
You can configure the
MultiPartFeature
by extendingjavax.ws.rs.core.Application
class and registering it by overriding thegetClasses
method (like I did in this application).Or you can configure the
MultiPartFeature
in your web.xml. You must add the following:<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
Hope it helps !
Hey i was able to build youre uploader using mvn clean install and it was successful but i dont understand what modification i need to make to be able to post file to the rest service everytime i upload a file it says error. Any help would be great my project is due very soon and this would be awesome to include.
THanks!
Hello Omar, Is the error originating from the server, or the client? And can you please provide more detail about which error you’re receiving?
Hi!, I am testing some examples with the same bad result, your code is the last one. My problem is that the event progress is triggered only one time. For example, following your code, the event is triggered at the beginning and the progress bar is filled with the 100% value, but the file is not uploaded yet. Any idea? Thanks in advance.