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.
Objectives:
- How to upload single or multiple files using jquery.fileupload.js?
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()); } 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