Spring MVC + jQuery File Upload (multiple, drag&drop, progress…)

jQuery-File-Upload js library is “so far” the most elegant, smart, simple..etc js library I have used to upload files. Simply it is amazing. It supports async upload “Ajax”, multiple files upload, drag&drop, progress update and a lot more. Here we will see an example of uploading multiple files to Spring MVC app. Bootstrap has been used in this example to make the UI more attractive.

spring-mvc-jquery-file-upload

Objectives:

Environment:

  • Firefox & Chrome (IE was not tested!)
  • Maven
  • Eclipse
  • Jetty (or other java server)

Libraries:

Java Libraries:

  • Spring Framework
  • Apache common io & fileupload
  • Jackson
  • Common logging
  • Other

check .classpath file for complete list of jars

pom.xml

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
		</dependency>
		<dependency>
		<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.2</version>
		</dependency>
	    <dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.4</version>
		</dependency>
        <dependency>
		<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-mapper-asl</artifactId>
			<version>1.9.12</version>
		</dependency>
        <dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-core-asl</artifactId>
			<version>1.9.12</version>
		</dependency>
	</dependencies>

Javascript Libraries:

<script src="js/jquery.1.9.1.min.js"></script>
<script src="js/vendor/jquery.ui.widget.js"></script>
<script src="js/jquery.iframe-transport.js"></script>
<script src="js/jquery.fileupload.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<link href="bootstrap/css/bootstrap.css" type="text/css" rel="stylesheet" />

The example was designed to run with simple Maven command, just download the source code and run it.

>mvn jetty:run

( 1 ) Backend – Java Project “Spring MVC”

This project was generate using Maven and converted into Eclipse

For the backend we have:

  • 2 Java files (FileController.java & FileMeta.java)
  • 2 XML files (rest-servlet.xml & web.xml)
  • src/main/java/com/hmkcode/spring/mvc/controllers/FileController.java
package com.hmkcode.spring.mvc.controllers;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import com.hmkcode.spring.mvc.model.FileMeta;

@Controller
@RequestMapping("/controller")
public class FileController {

	LinkedList<FileMeta> files = new LinkedList<FileMeta>();
	FileMeta fileMeta = null;
	/***************************************************
	 * URL: /rest/controller/upload  
	 * upload(): receives files
	 * @param request : MultipartHttpServletRequest auto passed
	 * @param response : HttpServletResponse auto passed
	 * @return LinkedList<FileMeta> as json format
	 ****************************************************/
	@RequestMapping(value="/upload", method = RequestMethod.POST)
	public @ResponseBody LinkedList<FileMeta> upload(MultipartHttpServletRequest request, HttpServletResponse response) {

		//1. build an iterator
		 Iterator<String> itr =  request.getFileNames();
		 MultipartFile mpf = null;

		 //2. get each file
		 while(itr.hasNext()){

			 //2.1 get next MultipartFile
			 mpf = request.getFile(itr.next()); 
			 System.out.println(mpf.getOriginalFilename() +" uploaded! "+files.size());

			 //2.2 if files > 10 remove the first from the list
			 if(files.size() >= 10)
				 files.pop();

			 //2.3 create new fileMeta
			 fileMeta = new FileMeta();
			 fileMeta.setFileName(mpf.getOriginalFilename());
			 fileMeta.setFileSize(mpf.getSize()/1024+" Kb");
			 fileMeta.setFileType(mpf.getContentType());

			 try {
				fileMeta.setBytes(mpf.getBytes());

                 // copy file to local disk (make sure the path "e.g. D:/temp/files" exists)			
                 FileCopyUtils.copy(mpf.getBytes(), new FileOutputStream("D:/temp/files/"+mpf.getOriginalFilename()));

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			 //2.4 add to files
			 files.add(fileMeta);
		 }
		// result will be like this
		// [{"fileName":"app_engine-85x77.png","fileSize":"8 Kb","fileType":"image/png"},...]
		return files;
	}
	/***************************************************
	 * URL: /rest/controller/get/{value}
	 * get(): get file as an attachment
	 * @param response : passed by the server
	 * @param value : value from the URL
	 * @return void
	 ****************************************************/
	@RequestMapping(value = "/get/{value}", method = RequestMethod.GET)
	 public void get(HttpServletResponse response,@PathVariable String value){
		 FileMeta getFile = files.get(Integer.parseInt(value));
		 try {		
			 	response.setContentType(getFile.getFileType());
			 	response.setHeader("Content-disposition", "attachment; filename=\""+getFile.getFileName()+"\"");
		        FileCopyUtils.copy(getFile.getBytes(), response.getOutputStream());
		 }catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
		 }
	 }
}

src/main/java/com/hmkcode/spring/mvc/model/FileMeta.java

package com.hmkcode.spring.mvc.model;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;

//ignore "bytes" when return json format
@JsonIgnoreProperties({"bytes"}) 
public class FileMeta {

	private String fileName;
	private String fileSize;
	private String fileType;

	private byte[] bytes;

         //setters & getters
}

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
id="WebApp_ID" version="2.5">
  <display-name>Archetype Created Web Application</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

  <servlet>
  	<servlet-name>rest</servlet-name>            
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>rest</servlet-name>
  	<url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

src/main/webapp/WEB-INF/rest-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans-3.0.xsd


http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-3.0.xsd


http://www.springframework.org/schema/mvc


http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<context:component-scan base-package="com.hmkcode.spring.mvc.controllers" />
 	<mvc:annotation-driven />
 	<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
</beans>

( 2 ) Frontend (HTML, CSS, JS)

  • 1 HTML file (index.html)
  • 1 JS file (myuploadfunction.js)
  • 1 CSS file (dropzone.css)

Other .js, .css, images files are downloaded

  • /scr/main/webapp/index.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>jQuery File Upload Example</title>
<script src="js/jquery.1.9.1.min.js"></script>

<script src="js/vendor/jquery.ui.widget.js"></script>
<script src="js/jquery.iframe-transport.js"></script>
<script src="js/jquery.fileupload.js"></script>

<!-- bootstrap just to have good looking page -->
<script src="bootstrap/js/bootstrap.min.js"></script>
<link href="bootstrap/css/bootstrap.css" type="text/css" rel="stylesheet" />

<!-- we code these -->
<link href="css/dropzone.css" type="text/css" rel="stylesheet" />
<script src="js/myuploadfunction.js"></script>
</head>

<body>
<h1>Spring MVC - jQuery File Upload</h1>
<div style="width:500px;padding:20px">

    <input id="fileupload" type="file" name="files[]" data-url="rest/controller/upload" multiple>

    <div id="dropzone">Drop files here</div>

    <div id="progress">
        <div style="width: 0%;"></div>
    </div>

    <table id="uploaded-files">
        <tr>
            <th>File Name</th>
            <th>File Size</th>
            <th>File Type</th>
            <th>Download</th>
        </tr>
    </table>

</div>
</body> 
</html>
  • /scr/main/webapp/js/myuploadfunction.js
$(function () {
    $('#fileupload').fileupload({
        dataType: 'json',

        done: function (e, data) {
        	$("tr:has(td)").remove();
            $.each(data.result, function (index, file) {

                $("#uploaded-files").append(
                		$('<tr/>')
                		.append($('<td/>').text(file.fileName))
                		.append($('<td/>').text(file.fileSize))
                		.append($('<td/>').text(file.fileType))
                		.append($('<td/>').html("<a href='rest/controller/get/"+index+"'>Click</a>"))
                		)//end $("#uploaded-files").append()
            }); 
        },

        progressall: function (e, data) {
	        var progress = parseInt(data.loaded / data.total * 100, 10);
	        $('#progress .bar').css(
	            'width',
	            progress + '%'
	        );
   		},

		dropZone: $('#dropzone')
    });
});
  • /scr/main/webapp/css/dropzone.css
#dropzone {
    background: #ccccc;
    width: 150px;
    height: 50px;
    line-height: 50px;
    text-align: center;
    font-weight: bold;
}
#dropzone.in {
    width: 600px;
    height: 200px;
    line-height: 200px;
    font-size: larger;
}
#dropzone.hover {
    background: lawngreen;
}
#dropzone.fade {
    -webkit-transition: all 0.3s ease-out;
    -moz-transition: all 0.3s ease-out;
    -ms-transition: all 0.3s ease-out;
    -o-transition: all 0.3s ease-out;
    transition: all 0.3s ease-out;
    opacity: 1;
}

( 3 ) Run the Example

./spring-mvc-jquery-file-upload>mvn jetty:run

http://localhost:8080/spring-mvc-jquery-file-upload/

Source Code @ github

Related posts from Spring Framework Tutorial

11 thoughts on “Spring MVC + jQuery File Upload (multiple, drag&drop, progress…)

  1. Chintan Patel

    Hi,

    I am working on java – spring mvc. I am submitting my data through JSON.

    What I want to do is:
    I want to upload an image instantly in to temporary folder on my server using , and show what is to be uploaded.

    On final submit all data including uploaded image path (temporary folder’ path) will be passed to my spring controller through JSON object, then transfer image from temporary directory to actual directory & saved all data in to a database.

    There are few questions:
    1. Where the file is to be uploaded (location)?
    2. Where to give folder path?

    I would be appreciated if you can hep me.

    Thanks in advance.

  2. Sridhar

    Above code segment is working great. But the problem is in IE once uploaded, its giving as downloadable file with unknown format(Some issue with IE in uploading).
    Can you please confirm these scripts will work IE also?

  3. Majid

    Hi,
    Nice tutorial, Can you please tell me where are the uploaded files located in my PC ?
    thanks

    1. hani.hmk Post author

      Hi,
      For this example, we don’t save uploaded files, we keep them on the memory.

        1. hani.hmk Post author

          Hi,
          I updated the “FileController.java” to copy files on local disk

          // copy file to local disk (make sure the path “e.g. D:/temp/files” exists)
          FileCopyUtils.copy(mpf.getBytes(), new FileOutputStream(“D:/temp/files/”+mpf.getOriginalFilename()));

  4. Dante

    Hi

    That’s awesome, It works like a charm but I need to separate the content server from the server which hosts my web application. So I tried to test this in two separate instance of tomcat (that should be two different domains in the real app) and I changed the “data-url” to ” http://localhost:8084/rest/controller/upload” instead of “rest/controller/upload” but I faced with this error:

    “XMLHttpRequest cannot load http://localhost:8084/rest/controller/upload. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.”

    I appreciate your comment ;)

  5. Latifaah

    Hi,
    I downloaded this project a while ago, it was working, I just downloaded it today , tried it in eclipse but I got this error :

    Description Resource Path Location Type
    Project ‘spring-mvc-jquery-file-upload’ is missing required source folder: ‘src/main/resources’ spring-mvc-jquery-file-upload Build path Build Path Problem

    Please your help is appreciated.
    thanks

Comments are closed.