Saturday 26 October 2013

Spring MVC Ajax file upload

Uploading a file using standard Spring MVC HTML form is straightforward, but in some cases if the solution requires to upload a file and display an image using AJAX request - requires a little bit more work to be done. Lets image that application is all set up, contains all necessary Spring dependencies and there are only AJAX file upload bits missing. First thing you need is an HTML form something like this:

<form id="idUploadLogoForm" enctype="multipart/form-data">
 
    <div>
 <label>Logo:</label>                
 <div id="idImagePlaceHolder"></div>
    </div>
 
    <div>
 <label for="idLogoFile">Choose your logo:</label>
 <input id="idLogoFile" type="file" name="logo"/>
    </div>   

</form>

<button id="idUploadLogoButton">Upload Logo</button>

Next thing needed is a class which will be the holder of Base64 encoded image's content. The class looks like this:

import com.fasterxml.jackson.annotation.JsonProperty;

import java.io.Serializable;

public class JsonLogo implements Serializable {

    @JsonProperty
    private String image;

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }
 
}

@JsonProperty annotation comes from com.fasterxml package, this requires some dependencies to be included to support JSON processing in Spring MVC. These dependencies are:

<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-annotations</artifactId>
 <version>2.2.2</version>
 <scope>compile</scope>
</dependency>
<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-core</artifactId>
 <version>2.2.2</version>
 <scope>compile</scope>
</dependency>
<dependency>
 <groupId>com.fasterxml.jackson.core</groupId>
 <artifactId>jackson-databind</artifactId>
 <version>2.2.2</version>
 <scope>compile</scope>
</dependency>

Next step is to create a method in Spring controller to be ready to accepts requests of file upload. This method looks like this:

@RequestMapping(value = "upload-logo", method = RequestMethod.POST)
@ResponseBody
public JsonLogo uploadLogo(MultipartHttpServletRequest request) {
    try {
 Iterator<String> itr = request.getFileNames();
 MultipartFile file = request.getFile(itr.next());

 JsonLogo logo = new JsonLogo();
 if (multipartFile.getBytes().length > 0) {   
     logo.setImage(new String(Base64.encodeBase64(multipartFile.getBytes())));
 }   
 return logo;
    } catch (Exception e) {
 //Handle exception if any
    }
    return null;
}

For encoding Base64 Apache Commons Codec library is used.
The last thing is a jQuery bit which performs AJAX POST request to a Spring controller:

$('#idUploadLogoButton').on('click', function () {
 var form = new FormData(document.getElementById('idUploadLogoForm'));
 $.ajax({
  url: "upload-logo.htm",
  data: form,
  dataType: 'text',
  processData: false,
  contentType: false,
  type: 'POST',
  success: function (response) {
      var data = jQuery.parseJSON(response);   
      $('#idImagePlaceHolder').html('<img src="data:image/png;base64,' + data.JsonLogo.image + '"/>');   
  },
  error: function (jqXHR) {
   //Error handling
  }
 });
});

After submitting this request uploaded image will appear in an HTML div which is a placeholder for it.