Wednesday, June 20, 2012

File Upload with Spring and jQuery (Part 1)

Introduction

In this article, we will study how to do file uploads with Spring and jQuery. We will learn how to attach multiple files and use the jQuery-File-Upload plugin to provide a seamless file upload experience.


Dependencies


Github

To access the source code, please visit the project's Github repository (click here)

Functional Specs

Let's define our application's requirements:
  • Create a simple form where users can upload multiple files
  • Users should be able to add an owner name and description for each upload
  • Provide an AJAX-like experience

Here's our Use Case diagram:


[User]-(Add files)
[User]-(Upload)

File Upload Strategy
To achieve an AJAX-like experience we have to resort to a different strategy when uploading and sending form data. Instead of sending all form data in one go, we will upload the file separately (behind the scenes without user intervention).

Here are the steps:
  1. User fills-in the form's text inputs, i.e owner and description
  2. User clicks on "Add a file" link. Browsers for a file and attaches it
  3. Behind the scene, the form uploads the file to the server. The server saves the file and returns the file details, i.e filename and file size
  4. User clicks on "Upload" button. The form does a POST action to send the form data. But it never sends the file itself. Instead it sends the filename that was saved by the server. The user is tricked into thinking that the file hasn't been uploaded yet.

Screenshots

Let's preview how our application will look like after it has been completed. This is also a good way to clarify further our application's specs

This is the entry page where users can upload files. Users are allowed to attach multiple files.


Upload Form

When attaching multiple files, this is the expected output.


Multiple files attachment

This alert shows whenever files have been successfully uploaded.


Successful upload alert

This alerts shows whenever files have failed to be uploaded.


Form fields cleared alert

Next

In the next section, we will start writing the Java classes. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: File Upload with Spring and jQuery (Part 1) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

File Upload with Spring and jQuery (Part 2)

Review

In the previous section, we have laid down the functional specs of the application. In this section, we will start writing the Java classes and discuss the project's structure.


Project Structure

Our application is a Maven project which means our project follows the Maven structure.

Here's a preview of our project's structure:





Domain Layer

The domain layer contains a Message class that is used as a container to hold file details.

Message.java
package org.krams.domain;
import java.io.Serializable;
public class Message implements Serializable {
private static final long serialVersionUID = -4093981756240899937L;
private String owner;
private String description;
private String filename;
public Message() {
super();
}
public Message(String owner, String description, String filename) {
super();
this.owner = owner;
this.description = description;
this.filename = filename;
}
...getters/setters
}
view raw Message.java hosted with ❤ by GitHub


The domain layer also contains an UploadedFile class which is used for sending file information after it has been processed by the controller.

UploadedFile.java
package org.krams.domain;
import java.io.Serializable;
public class UploadedFile implements Serializable {
private static final long serialVersionUID = -38331060124340967L;
private String name;
private Integer size;
private String url;
private String thumbnail_url;
private String delete_url;
private String delete_type;
public UploadedFile() {
super();
}
public UploadedFile(String name, Integer size, String url) {
super();
this.name = name;
this.size = size;
this.url = url;
}
public UploadedFile(String name, Integer size, String url,
String thumbnail_url, String delete_url, String delete_type) {
super();
this.name = name;
this.size = size;
this.url = url;
this.thumbnail_url = thumbnail_url;
this.delete_url = delete_url;
this.delete_type = delete_type;
}
...getters/setters
}


Controller Layer

The controller layer contains a simple controller that serves a form for uploading files. There are two important endpoints here:
  • /message - processes the file descriptions
  • /file - receives the files themselves

To simplify this tutorial, we're not persisting the messages and files in a database or to the disk.

UploadController.java
package org.krams.controller;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.krams.domain.Message;
import org.krams.domain.UploadedFile;
import org.krams.response.StatusResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
@Controller
@RequestMapping("/upload")
public class UploadController {
private static Logger logger = Logger.getLogger("controller");
@RequestMapping
public String form() {
return "form";
}
@RequestMapping(value="/message", method=RequestMethod.POST)
public @ResponseBody StatusResponse message(@RequestBody Message message) {
// Do custom steps here
// i.e. Persist the message to the database
logger.debug("Service processing...done");
return new StatusResponse(true, "Message received");
}
@RequestMapping(value="/file", method=RequestMethod.POST)
public @ResponseBody List<UploadedFile> upload(
@RequestParam("file") MultipartFile file) {
// Do custom steps here
// i.e. Save the file to a temporary location or database
logger.debug("Writing file to disk...done");
List<UploadedFile> uploadedFiles = new ArrayList<UploadedFile>();
UploadedFile u = new UploadedFile(file.getOriginalFilename(),
Long.valueOf(file.getSize()).intValue(),
"http://localhost:8080/spring-fileupload-tutorial/resources/"+file.getOriginalFilename());
uploadedFiles.add(u);
return uploadedFiles;
}
}


Others

StatusResponse is used to determine the status of a request, and includes an error message if any.

StatusResponse.java
package org.krams.response;
import java.util.ArrayList;
import java.util.List;
/**
* A POJO containing the status of an action and a {@link List} of messages.
* This is mainly used as a DTO for the presentation layer
*/
public class StatusResponse {
private Boolean success;
private List<String> message;
public StatusResponse() {
this.message = new ArrayList<String>();
}
public StatusResponse(Boolean success) {
super();
this.success = success;
this.message = new ArrayList<String>();
}
public StatusResponse(Boolean success, String message) {
super();
this.success = success;
this.message = new ArrayList<String>();
this.message.add(message);
}
public StatusResponse(Boolean success, List<String> message) {
super();
this.success = success;
this.message = message;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public List<String> getMessage() {
return message;
}
public void setMessage(String message) {
this.message.add(message);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (String mess: message) {
sb.append(mess +", ");
}
return "StatusResponse [success=" + success + ", message=" + sb.toString()
+ "]";
}
}


Next

We've completed writing our Java classes. In the next section, we will start writing the configuration files. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: File Upload with Spring and jQuery (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

File Upload with Spring and jQuery (Part 3)

Review

In the previous section, we have implemented the Java classes. In this section, we will start writing the configuration files.


Configuration

To complete our application, here are the important configuration files that needs to be declared:
  • applicationContext.xml
  • spring-servlet.xml
  • web.xml

applicationContext.xml
Pay attention to the CommonsMultipartResolver and the MappingJacksonJsonView.
  • The CommonsMultipartResolver is a requirement for processing MultipartFile files
  • The MappingJacksonJsonView is required for serializing JSON responses properly. The extractValueFromSingleKeyModel is meant to remove the wrapper object when responding with single key model (see the Stack Overflow link for details).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<context:property-placeholder properties-ref="deployProperties" />
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config />
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
For example @Controller and @Service. Make sure to set the correct base-package -->
<context:component-scan base-package="org.krams" />
<!-- Configures the annotation-driven Spring MVC Controller programming model.
Note that, with Spring 3.0, this tag works in Servlet MVC only! -->
<mvc:annotation-driven />
<!-- Configure the multipart resolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:maxUploadSize="1000000"/>
<!-- http://stackoverflow.com/questions/6479712/why-is-jackson-wrapping-my-objects-with-an-extra-layer-named-after-the-class -->
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"
p:extractValueFromSingleKeyModel="true" />
<mvc:resources mapping="/resources/**" location="/resources/" />
<!-- Imports logging configuration -->
<import resource="trace-context.xml"/>
<bean id="deployProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/spring.properties" />
</beans>


spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- Declare a view resolver -->
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" p:order="1"/>
</beans>


web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring File Upload Tutorial</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
view raw web.xml hosted with ❤ by GitHub


Next

In the next section, we will discuss the HTML form for uploading files. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: File Upload with Spring and jQuery (Part 3) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

File Upload with Spring and jQuery (Part 4)

Review

In the previous section, we have written the configuration files. In this section, we will write the HTML form for uploading files.


To achieve a seamless AJAX-like file upload experience, we will be using blueimp's jQuery-File-Upload plugin for jQuery.

What is jQuery-File-Upload?

File Upload widget with multiple file selection, drag&drop support, progress bars and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads.

Source: https://github.com/blueimp/jQuery-File-Upload

To test-drive this plugin, please visit the following demos:

For our purposes, we will follow the minimal setup guide, so that we can create our custom UI and eliminate extraneous steps.

Warning!
Make sure to test the plugin on different browsers. Not all browsers behave the same way. In my opinion, Chrome and Firefox are the best for development.

Html Form

Here's our upload form again:

Upload Form

Here's the full source:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:url value="/upload/message" var="messageUploadUrl"/>
<c:url value="/upload/file" var="fileUploadUrl"/>
<html>
<head>
<link rel="stylesheet" type="text/css" media="screen" href='<c:url value="/resources/css/jquery-ui/pepper-grinder/jquery-ui-1.8.16.custom.css"/>'/>
<link rel="stylesheet" type="text/css" media="screen" href='<c:url value="/resources/css/style.css"/>'/>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-1.6.4.min.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-ui-1.8.16.custom.min.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/util.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/vendor/jquery.ui.widget.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/jquery.iframe-transport.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/jquery.fileupload.js"/>'></script>
<title>Upload</title>
<script type='text/javascript'>
$(function() {
init();
});
function init() {
$('input:button').button();
$('#submit').button();
$('#uploadForm').submit(function(event) {
event.preventDefault();
$.postJSON('${messageUploadUrl}', {
owner: $('#owner').val(),
description: $('#description').val(),
filename: getFilelist()
},
function(result) {
if (result.success == true) {
dialog('Success', 'Files have been uploaded!');
} else {
dialog('Failure', 'Unable to upload files!');
}
});
});
$('#reset').click(function() {
clearForm();
dialog('Success', 'Fields have been cleared!');
});
$('#upload').fileupload({
dataType: 'json',
done: function (e, data) {
$.each(data.result, function (index, file) {
$('body').data('filelist').push(file);
$('#filename').append(formatFileDisplay(file));
$('#attach').empty().append('Add another file');
});
}
});
// Technique borrowed from http://stackoverflow.com/questions/1944267/how-to-change-the-button-text-of-input-type-file
// http://stackoverflow.com/questions/210643/in-javascript-can-i-make-a-click-event-fire-programmatically-for-a-file-input
$("#attach").click(function () {
$("#upload").trigger('click');
});
$('body').data('filelist', new Array());
}
function formatFileDisplay(file) {
var size = '<span style="font-style:italic">'+(file.size/1000).toFixed(2)+'K</span>';
return file.name + ' ('+ size +')<br/>';
}
function getFilelist() {
var files = $('body').data('filelist');
var filenames = '';
for (var i=0; i<files.length; i<i++) {
var suffix = (i==files.length-1) ? '' : ',';
filenames += files[i].name + suffix;
}
return filenames;
}
function dialog(title, text) {
$('#msgbox').text(text);
$('#msgbox').dialog(
{ title: title,
modal: true,
buttons: {"Ok": function() {
$(this).dialog("close");}
}
});
}
function clearForm() {
$('#owner').val('');
$('#description').val('');
$('#filename').empty();
$('#attach').empty().append('Add a file');
$('body').data('filelist', new Array());
}
</script>
</head>
<body>
<h1 id='banner'>Upload Files</h1>
<div>
<form id='uploadForm'>
<fieldset>
<legend>Files</legend>
<label for='owner'>Owner:</label><input type='text' id='owner'/><br/>
<textarea name="description" id="description">Description here</textarea><br/>
<span id='filename'></span><br/>
<a href='#' id='attach'>Add a file</a><br/>
<input id="upload" type="file" name="file" data-url="${fileUploadUrl}" multiple style="opacity: 0; filter:alpha(opacity: 0);"><br/>
<input type='button' value='Reset' id='reset' />
<input type='submit' value='Upload' id='submit'/>
</fieldset>
</form>
</div>
<div id='msgbox' title='' style='display:none'></div>
</body>
</html>
view raw form.jsp hosted with ❤ by GitHub


Let's dissect this source and make sense of it.

Endpoint Urls
We've declared two global urls: messageUploadUrl and fileUploadUrl. These are the endpoints for uploading messages and files respectively.

<c:url value="/upload/message" var="messageUploadUrl"/>
<c:url value="/upload/file" var="fileUploadUrl"/>
view raw snippet.jsp hosted with ❤ by GitHub


Import Scripts
Based on the jQuery-File-Upload basic plugin guide, we need to import the following scripts: jquery.ui.widget.js, jquery.iframe-transport.js, and jquery.fileupload.js. The util.js contains a method for posting JSON objects. The remaining scripts are for the jQuery framework itself.

<script type='text/javascript' src='<c:url value="/resources/js/jquery-1.6.4.min.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-ui-1.8.16.custom.min.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/util.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/vendor/jquery.ui.widget.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/jquery.iframe-transport.js"/>'></script>
<script type='text/javascript' src='<c:url value="/resources/js/jquery-fileupload/jquery.fileupload.js"/>'></script>
view raw snippet.jsp hosted with ❤ by GitHub


Initialization Function
This function contains the following initialization steps:
  • Beautify buttons
  • Attach submit function
  • Attach clear function
  • Attach file upload function
  • Initialize filelist data

function init() {
$('input:button').button();
$('#submit').button();
$('#uploadForm').submit(function(event) {
event.preventDefault();
$.postJSON('${messageUploadUrl}', {
owner: $('#owner').val(),
description: $('#description').val(),
filename: getFilelist()
},
function(result) {
if (result.success == true) {
dialog('Success', 'Files have been uploaded!');
} else {
dialog('Failure', 'Unable to upload files!');
}
});
});
$('#reset').click(function() {
clearForm();
dialog('Success', 'Fields have been cleared!');
});
$('#upload').fileupload({
dataType: 'json',
done: function (e, data) {
$.each(data.result, function (index, file) {
$('body').data('filelist').push(file);
$('#filename').append(formatFileDisplay(file));
$('#attach').empty().append('Add another file');
});
}
});
// Technique borrowed from http://stackoverflow.com/questions/1944267/how-to-change-the-button-text-of-input-type-file
// http://stackoverflow.com/questions/210643/in-javascript-can-i-make-a-click-event-fire-programmatically-for-a-file-input
$("#attach").click(function () {
$("#upload").trigger('click');
});
$('body').data('filelist', new Array());
}
view raw snippet.jsp hosted with ❤ by GitHub


File Display Function
This is used for formatting filenames. The output is similar to the following: filename (256.50K)

function formatFileDisplay(file) {
var size = '<span style="font-style:italic">'+(file.size/1000).toFixed(2)+'K</span>';
return file.name + ' ('+ size +')<br/>';
}
view raw snippet.jsp hosted with ❤ by GitHub


File List Function
This is used to retrieved the list of filenames. The output is comma-delimited, which is similar to the following: filename1.jpg,filename2.doc,filename3.txt

function getFilelist() {
var files = $('body').data('filelist');
var filenames = '';
for (var i=0; i<files.length; i<i++) {
var suffix = (i==files.length-1) ? '' : ',';
filenames += files[i].name + suffix;
}
return filenames;
}
view raw snippet.jsp hosted with ❤ by GitHub


Dialog Function
This is a helper function for displaying dialog boxes.

function dialog(title, text) {
$('#msgbox').text(text);
$('#msgbox').dialog(
{ title: title,
modal: true,
buttons: {"Ok": function() {
$(this).dialog("close");}
}
});
}
view raw snippet.jsp hosted with ❤ by GitHub


Clear Function
This clears the form of its contents.

function clearForm() {
$('#owner').val('');
$('#description').val('');
$('#filename').empty();
$('#attach').empty().append('Add a file');
$('body').data('filelist', new Array());
}
view raw snippet.jsp hosted with ❤ by GitHub


Form
This is the form itself. Notice we've set the file input's opacity to zero to hide it and make it work under Safari and Opera (try removing the opacity and hide the file input via display:none to see an unwanted effect).

<form id='uploadForm'>
<fieldset>
<legend>Files</legend>
<label for='owner'>Owner:</label><input type='text' id='owner'/><br/>
<textarea name="description" id="description">Description here</textarea><br/>
<span id='filename'></span><br/>
<a href='#' id='attach'>Add a file</a><br/>
<input id="upload" type="file" name="file" data-url="${fileUploadUrl}" multiple style="opacity: 0; filter:alpha(opacity: 0);"><br/>
<input type='button' value='Reset' id='reset' />
<input type='submit' value='Upload' id='submit'/>
</fieldset>
</form>
view raw snippet.jsp hosted with ❤ by GitHub


By setting the file input's opacity to 0, we have successfully hidden the input. But how do we access it now? Notice the "Add a file" link? We've attached a click trigger function on that link to fire up the file input. If we removed the opacity, here's what we will see:

Chrome 19.0.1084.56


Firefox 9.0.1


Internet Explorer 9.0.8112.16421


Browser Bugs

Unfortunately, even though the jQuery-File-Upload plugin has great cross-browser support, HTML5 and traditional, there are still bugs and unwanted behavior. Maybe it's a configuration issue or a browser issue. In other words, always tests before you deploy to production. Here's what I've discovered so far:

Internet Explorer 9.0.8112.16421
  • Does not upload file
  • Does not upload message

Opera 11.60
  • Attach link does not work if file input has display:none
  • To make it work, set the opacity instead

Safari 5.1.2
  • Attach link does not work if file input has display:none
  • To make it work, set the opacity instead

Next

In the next section, we will build and run the application using Maven, and show how to import the project in Eclipse. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: File Upload with Spring and jQuery (Part 4) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

File Upload with Spring and jQuery (Part 5)

Review

We have just completed our application! In the previous sections, we have discussed the functional specs, created the Java classes, declared the configuration files, and wrote the HTML files. In this section, we will build and run the application using Maven, and show how to import the project in Eclipse.


Running the Application

Access the source code

To download the source code, please visit the project's Github repository (click here)

Building with Maven

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn tomcat:run
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'tomcat'.
    [INFO] artifact org.codehaus.mojo:tomcat-maven-plugin: checking for updates from central
    [INFO] artifact org.codehaus.mojo:tomcat-maven-plugin: checking for updates from snapshots
    [INFO] ------------------------------------------
    [INFO] Building spring-fileupload-tutorial Maven Webapp
    [INFO]    task-segment: [tomcat:run]
    [INFO] ------------------------------------------
    [INFO] Preparing tomcat:run
    [INFO] [apt:process {execution: default}]
    [INFO] [resources:resources {execution: default-resources}]
    [INFO] [tomcat:run {execution: default-cli}]
    [INFO] Running war on http://localhost:8080/spring-fileupload-tutorial
    Jun 20, 2012 8:35:14 PM org.apache.catalina.startup.Embedded start
    INFO: Starting tomcat server
    Jun 20, 2012 8:35:14 PM org.apache.catalina.core.StandardEngine start
    INFO: Starting Servlet Engine: Apache Tomcat/6.0.29
    Jun 20, 2012 8:35:15 PM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring root WebApplicationContext
    Jun 20, 2012 8:35:17 PM org.apache.coyote.http11.Http11Protocol init
    INFO: Initializing Coyote HTTP/1.1 on http-8080
    Jun 20, 2012 8:35:17 PM org.apache.coyote.http11.Http11Protocol start
    INFO: Starting Coyote HTTP/1.1 on http-8080
    
  5. Note: If the project will not build due to missing repositories, please enable the repositories section in the pom.xml!

Access the Entry page

  1. Follow the steps with Building with Maven
  2. Open a browser
  3. Enter the following URL (8080 is the default port for Tomcat):
    http://localhost:8080/spring-fileupload-tutorial/upload

Attach a file

  1. Click on "Add a file"
  2. Browse for a file and add it. Check the logs and you should see something similar to the following:
    [DEBUG] [tomcat-http--24 02:15:20] (TraceInterceptor.java:writeToLog:21) Entering UploadController.upload(org.springframework.web.multipart.commons.CommonsMultipartFile@d1258b)
    [DEBUG] [tomcat-http--24 02:15:20] (UploadController.java:upload:43) Writing file to disk...done
    [DEBUG] [tomcat-http--24 02:15:20] (TraceInterceptor.java:writeToLog:21) Leaving UploadController.upload(): [UploadedFile [name=Tulips.jpg, size=620888, url=http://localhost:8080/spring-fileupload-tutorial/resources/Tulips.jpg, thumbnail_url=null, delete_url=null, delete_type=null]]
    

Attach multiple files

  1. Click on "Add another file"
  2. Browse for files and add them. Check the logs and you should see something similar to the following:
    [DEBUG] [tomcat-http--26 02:15:42] (TraceInterceptor.java:writeToLog:21) Entering UploadController.upload(org.springframework.web.multipart.commons.CommonsMultipartFile@7783ea)
    [DEBUG] [tomcat-http--26 02:15:42] (UploadController.java:upload:43) Writing file to disk...done
    [DEBUG] [tomcat-http--26 02:15:42] (TraceInterceptor.java:writeToLog:21) Leaving UploadController.upload(): [UploadedFile [name=Hydrangeas.jpg, size=595284, url=http://localhost:8080/spring-fileupload-tutorial/resources/Hydrangeas.jpg, thumbnail_url=null, delete_url=null, delete_type=null]]
    [DEBUG] [tomcat-http--27 02:15:42] (TraceInterceptor.java:writeToLog:21) Entering UploadController.upload(org.springframework.web.multipart.commons.CommonsMultipartFile@85ec1b)
    [DEBUG] [tomcat-http--27 02:15:42] (UploadController.java:upload:43) Writing file to disk...done
    [DEBUG] [tomcat-http--27 02:15:42] (TraceInterceptor.java:writeToLog:21) Leaving UploadController.upload(): [UploadedFile [name=Jellyfish.jpg, size=775702, url=http://localhost:8080/spring-fileupload-tutorial/resources/Jellyfish.jpg, thumbnail_url=null, delete_url=null, delete_type=null]]
    

Upload the files

  1. Click on "Upload". Check the logs and you should see something similar to the following:
    [DEBUG] [tomcat-http--30 02:16:20] (TraceInterceptor.java:writeToLog:21) Entering UploadController.message(Message [owner=John Smith, description=These are my files. I owned them., filename=Tulips.jpg,Hydrangeas.jpg,Jellyfish.jpg])
    [DEBUG] [tomcat-http--30 02:16:20] (UploadController.java:message:33) Service processing...done
    [DEBUG] [tomcat-http--30 02:16:20] (TraceInterceptor.java:writeToLog:21) Leaving UploadController.message(): StatusResponse [success=true, message=Message received, ]
    

Note:

When adding a file via the "Add a file" link, the file is automatically uploaded to the backend. The backend responds with an object containing the filename (and other file details). When you click on the "Upload" button, the form data is sent along with the filename. The file itself is never uploaded in this second step because it's already in the server!

Import the project in Eclipse

  1. Ensure Maven is installed
  2. Open a command window (Windows) or a terminal (Linux/Mac)
  3. Run the following command:
    mvn eclipse:eclipse -Dwtpversion=2.0
  4. You should see the following output:
    [INFO] Scanning for projects...
    [INFO] Searching repository for plugin with prefix: 'eclipse'.
    [INFO] org.apache.maven.plugins: checking for updates from central
    [INFO] org.apache.maven.plugins: checking for updates from snapshots
    [INFO] org.codehaus.mojo: checking for updates from central
    [INFO] org.codehaus.mojo: checking for updates from snapshots
    [INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for updates from central
    [INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for updates from snapshots
    [INFO] -----------------------------------------
    [INFO] Building spring-fileupload-tutorial Maven Webapp
    [INFO]    task-segment: [eclipse:eclipse]
    [INFO] -----------------------------------------
    [INFO] Preparing eclipse:eclipse
    [INFO] No goals needed for project - skipping
    [INFO] [eclipse:eclipse {execution: default-cli}]
    [INFO] Adding support for WTP version 2.0.
    [INFO] -----------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] -----------------------------------------
    
    This command will add the following files to your project:
    .classpath
    .project
    .settings
    target
    You may have to enable "show hidden files" in your file explorer to view them
  5. Open Eclipse and import the project

Conclusion

That's it! We've have successfully completed our file upload application with Spring and jQuery-File-Upload plugin. We've learned how to setup an HTML form for file uploads with AJAX-like experience.

I hope you've enjoyed this tutorial. Don't forget to check my other tutorials at the Tutorials section.

Revision History


Revision Date Description
1 June 20 2012 Uploaded tutorial
2 June 26 2012 Corrected wrong entry url
3 June 27 2012 Corrected wrong entry url again

StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: File Upload with Spring and jQuery (Part 5) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share