Friday, September 5, 2014

How to upload file data using Spring MVC, Angular JS and Kendo UI

Uploading a file in Spring is one of those super important things to know about, which, for some reason, none of my past projects required me to do. And suddenly I have to asynchronously upload a file using Kendo UI, AngularJS and Spring MVC. The widget I use in Kendo UI is "Upload" Here are the steps we need to do.


  1. Enable Spring’s built-in multipart handling.
  2. Create a method in the Spring controller that will handle the multipart POST.
  3. Create the Kendo UI Upload widget.



1. Enable Spring’s built-in multipart handling
By default, Spring does no multipart handling. This is done by adding a "multipart resolver" bean. I chose to use a multipart resolver that uses Apache Commons FileUpload package (which is mature and robust). First include the package. I use Maven. So do it like this:
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.2.1</version>
</dependency>

Then I add the multipart resolver into the Spring configuration:
    <bean id="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
(the id attribute was not needed in my case)



2. Create a method in the Spring controller that will handle the multipart POST.
This method will only accept POST calls. It will have a MultiPart parameter, which contains (or contains information about) the file data. All the temporary file data will be cleared at the end of request processing.
@RequestMapping(value = "uploadFile", method = RequestMethod.POST)
@ResponseBody
public Map<String, String> uploadImage( @RequestParam("file") MultipartFile file, 
                                                     @RequestParam("username") String userName {

    InputStream inputStream = null;
    try {
        inputStream = file.getInputStream();
        // do something with inputStream here....
        inputStream = null;

    } finally {
        if (inputStream != null)
            inputStream.close();
        inputStream = null;
    }

    String extension = FilenameUtils.getExtension( file.getOriginalFilename() );

    final HashMap<String, String> ret = new HashMap<String, String>();
    ret.put( "someInfo", "foobar from uploadImage" );
    return ret;
}
Some interesting things happen here:

  • I also take in another @RequestParam. This is pretty typical of a file upload: to pass more information about the file to the server. Kendo UI's Upload widget's async upload will require a little tweaking to pass in additional information. More on that later.
  • I used MultipartFile::getInputStream() and MultipartFile::getOriginalFile(). These are typical usages of MultipartFile. The entire API is here
  • I also use Apache Commons awesome FilenameUtils and other Commons IO classes. In Java 6 and below, it's still the best way to do IO. When using Commons IO, remember to check if the method closes the stream/file. If not, you have to do it yourself. There are sometimes 2 similar methods to do something: 1 that closes the stream/file, and 1 that doesn't. Do yourself a favor. Use the first one.
  • Finally, I also return a Map. This will translate to an Object when returned back to AngularJS. Not all async uploads require return data. But if you do, here's how you do it. More on that later.

3. Create the Kendo UI Upload widget
Let's take a look at the widget:
<input kendo-upload
       name="file"
       type="file"
       k-multiple="false"
       k-show-file-list="false"
       k-upload="addMorePostParameters"
       k-success="onSuccess"
       k-error="onError"
       k-async="{ saveUrl: 'uploadFile', autoUpload: true }"
        />
In my case:

  • I set k-multiple to false.
  • I also don't want to show the file list
  • k-async contains the URL, and the setting to auto upload the file once the user selects it.
Check out all the configuration on Kendo UI's API page.

How do we pass in the username? That's where k-upload comes in. The API states that the k-upload event "fires when one or more files are about to be uploaded". Let's take a look at the AngularJS code:
$scope.addMorePostParameters = function (e) {
    e.data = {username: "gerard"};
};
The API also says that there is a data object "that will be sent to the save handler in the form of key/value pairs". So we use that to pass in the username String together with the file upload.

How do we access the return value (if any) which the server returned? k-success will do that for you.
$scope.onSuccess = function (e) {
    var someInfo = e.response.someInfo;
};
e.response.someInfo will contain the string "foobar from uploadImage".


I hope that helps.





5 comments:

  1. This is my first time visit on your site and i have bookmark this for again visit. thanks a lot of for share a appreciable post ..
    Angularjs Training In Hyderabad

    ReplyDelete
  2. Hi
    thanks a lot for your post but I need below dependency to work it fine


    commons-fileupload
    commons-fileupload
    1.2.1

    ReplyDelete
  3. I never comment on blogs and whatnot but I found this incredibly helpful. Thank you!

    ReplyDelete
  4. This comment has been removed by a blog administrator.

    ReplyDelete