Uploading file: HTML5, BackboneJS and RESTFul services

In my previous post, I have introduced the sample application Account Manager. In this new post I want to describe implementation details of a functionality aimed at uploading the account’s picture.

uploading

The starting point is the html page (in my architecture it is not a complete html page but a section of the entire page due to Underscore JS templating feature).

The first step is to declare an input tag of type = file, that defines a file-select field and a “Browse” button for file upload:

<input id="fileInput" type="file" name="fileInput">

To give Bootstrap style to input element (the actual release of Bootstrap doesn’t support this element yet) , I have used a custom style. More details here.

The result is the input is sorrounded by span element with specific styles:

<span class="file-input btn btn-block btn-primary btn-file">
  <input id="fileInput" type="file" name="fileInput">
</span>

The next step is to send the file to the RESTFul service for uploading. In the html page, I haven’t used the <form> tag and a button to submit the form/request (the input element is not inside a form element). For this purpose, I use a new feature of HTML5, that is FormData object. The data submit is done via JQuery.

The FormData object is one of the enhancements to XMLHttpRequest.  With the FormData object, you can create and send a set of key/value pairs and, optionally, files using XMLHttpRequest. When using this technique, the data is sent in the same format as if you’d submitted it via the form’s submit() method with the encoding type of multipart/form-data.

Let’s have a look at the code.

In my Backbone .js file for account’s details page, I have created a function saveFile:

saveFile: function() {
  var picture = $('input[name="fileInput"]')[0].files[0]; 
  var data = new FormData();
  data.append('file', picture);
  $.ajax({
    url: 'rest/accounts/upload/'+this.model.get('picture'),
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    type: 'POST',
    success: function(data){
      $('#loadingModal').modal('hide');
    },
    error: function(data){
      alert('no upload');
      $('#loadingModal').modal('hide');
    }
  });
},

The first thing is to retrieve the file uploaded and attached to the input element:

var picture = $('input[name="fileInput"]')[0].files[0];

Since you can upload more than one file, (I have implemented a check on number of file because you can upload only one picture a time for an account) I know the picture is at position zero of files array.

Next, I create a new FormData object and append the picture file as key/value data:

var data = new FormData();
data.append('file', picture);

Finally, I call the RESTFul POST method service, to save the picture on the server, via Ajax passing the FormData object:

$.ajax({
    url: 'rest/accounts/upload/'+this.model.get('picture'),
    data: data,
    ...
    type: 'POST',
    ...
});

Before calling the saveFile function, I do something on creating an unique filename for the picture that will be persisted on the server. So after browising and selecting the image file I create the filename using a timestamp

var filename = 'pictureId' + $.now() + '.' + this.getFileExt();

and getting the file extension with my function getFileExt:

getFileExt: function() {
 var files = $('input[name="fileInput"]')[0].files;
 var ext = null;
 if(files[0] != null){
  var filename = 
      files[0].name.replace(/\\/g, '/').replace(/.*\//, '');
  ext = filename.replace(/^.*\./, '').toLowerCase();
 } 
 return ext;
 },

and then I set the picture attribute value for the Backbone model, so it will be persisted in the field PICTURE of MySQL table ACCOUNT:

this.model.set("picture", filename);

so when the upload Rest service is called I can get the parameter filename with

url: 'rest/accounts/upload/'+this.model.get('picture'),

At this point, the picture file is going to be managed by RESTFul service:

@POST
@Path("/upload/{filename}") 
@Consumes("multipart/form-data")
public Response uploadFile( @PathParam("filename") 
                            String filename,
                            MultipartFormDataInput input) {
 String file = ""; 
 Map<String, List<InputPart>> uploadForm = 
                              input.getFormDataMap();
 List<InputPart> inputParts = uploadForm.get("file"); 
 for (InputPart inputPart : inputParts) {
   try {
      InputStream inputStream = 
                  inputPart.getBody(InputStream.class,null);
      byte [] bytes = IOUtils.toByteArray(inputStream);
      file = Consts.BASE_PATH_FILE + File.separator + filename; 
      writeFile(bytes,file);
   } catch (IOException e) {
      e.printStackTrace();
   }
 } 
 return Response.
        status(200).
        entity("Uploaded file name : " + file)
        .build(); 
}

The service accepts MIME media types multipart/form-data (value required when you are using forms that have a file upload control) and has one parameter that is the file name. The method gets the form data map and retrieve the file with the same key “file” used in data.append(‘file’, picture).

The remainder is to get an input stream and an array of bytes and to write the file in a path on the server. In my case, the path is the data directory on JBoss AS, retrieved with system getProperty:

Consts.BASE_PATH_FILE = 
       System.getProperty("jboss.server.data.dir");

The writeFile method is:

private void writeFile(byte[] content, String filename) 
throws IOException { 
  File file = new File(filename);
  if (!file.exists()) {
    file.createNewFile();
  }
  FileOutputStream fop = new FileOutputStream(file);
  fop.write(content);
  fop.flush();
  fop.close(); 
}

So far, I have uploaded the picture on the server, let’s see how to retrieve the picture when you visit the account detail page.

It is very simple, we use a RESTFul service calling it from scr attribute of  html img tag with the parameter picture of the backbone model, saved before file persistence:

<img src="<%= 'rest/accounts/picture/' + picture %>"/>

The REST service is:

@GET
@Path("/picture/{picturename}")
@Produces("image/png")
public Response getFile(@PathParam("picturename") 
                        String pictureName){
  String fileName = Consts.BASE_PATH_FILE + 
                    File.separator + pictureName;
  File file = new File(fileName);
  ResponseBuilder response = Response.ok((Object) file);
  response.header("Content-Disposition", 
                  "attachment; filename=image_from_server.png");
  return response.build();
}
Advertisements

3 thoughts on “Uploading file: HTML5, BackboneJS and RESTFul services

  1. Ben says:

    hi, something appears to have gone wrong with some of the formatting in the code snippets, for example this…

    url: ‘rest/accounts/upload/’ + this.model.get(‘picture data: data,

    maybe you’d like to fix it. Thanks

  2. Agiva Dillah says:

    nice post, is awesome.
    but why you use this.model.get(‘picture’) when using url json?
    tell me please i dont understand.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s