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>http://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/

jersey file upload HTML5 AJAX no file jersey file upload HTML5 AJAX progress jersey file upload HTML5 AJAX complete

References

Download

You may also like...

  • 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=[[email protected]]}, 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 in rover7c.rest.SegmentController contains empty path annotation.; source=’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)’, [WARNING] The (sub)resource method getSegments in rover7c.rest.SegmentController contains empty path annotation.; source=’public java.lang.String[] rover7c.rest.SegmentController.getSegments(java.lang.String,java.lang.String)’]

    • 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 extending javax.ws.rs.core.Application class and registering it by overriding the getClasses 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 !

  • omar

    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?