Spring MVC file upload example – Commons FileUpload

This article will shows you how to use CommonsMultipartResolver to handle file upload in Spring MVC web application.

Tools used :

  1. Spring 4.3.5.RELEASE
  2. commons-fileupload 1.3.2
  3. Maven 3
  4. Tomcat 7 or 8 and Jetty 8, 9
Note
For StandardServletMultipartResolver – file upload using Servlet 3.0 multipart request parsing, please refer to this Spring MVC file upload example

1. Project Structure

A standard Maven project structure.

spring-multi-file-upload-example-directory

2. Project Dependency

2.1 You need commons-fileupload.jar

pom.xml

    <!-- Apache Commons Upload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.2</version>
    </dependency>

2.2 Complete Maven Pom file.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong</groupId>
    <artifactId>spring-mvc-file-upload</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>Spring MVC file upload</name>

    <properties>
        <jdk.version>1.8</jdk.version>
        <spring.version>4.3.5.RELEASE</spring.version>
        <jstl.version>1.2</jstl.version>
        <servletapi.version>3.1.0</servletapi.version>
        <commons.fileupload.version>1.3.2</commons.fileupload.version>
        <logback.version>1.1.3</logback.version>
        <jcl.slf4j.version>1.7.12</jcl.slf4j.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
        </dependency>

        <!-- compile only, deployed container will provide this -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servletapi.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${jcl.slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- Apache Commons Upload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>${commons.fileupload.version}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>${jdk.version}</source>
                    <target>${jdk.version}</target>
                </configuration>
            </plugin>

            <!-- embedded Jetty server, for testing -->
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.2.11.v20150529</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <webApp>
                        <contextPath>/spring4upload</contextPath>
                    </webApp>
                </configuration>
            </plugin>

            <!-- configure Eclipse workspace -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                    <wtpversion>2.0</wtpversion>
                    <wtpContextName>/spring4upload</wtpContextName>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>
Terminal

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring MVC file upload 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ spring-mvc-file-upload ---
[INFO] com.mkyong:spring-mvc-file-upload:war:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-webmvc:jar:4.3.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:4.3.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:4.3.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-context:jar:4.3.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-core:jar:4.3.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-expression:jar:4.3.5.RELEASE:compile
[INFO] |  \- org.springframework:spring-web:jar:4.3.5.RELEASE:compile
[INFO] +- javax.servlet:jstl:jar:1.2:compile
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] +- org.slf4j:jcl-over-slf4j:jar:1.7.12:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.12:compile
[INFO] +- ch.qos.logback:logback-classic:jar:1.1.3:compile
[INFO] |  \- ch.qos.logback:logback-core:jar:1.1.3:compile
[INFO] \- commons-fileupload:commons-fileupload:jar:1.3.2:compile
[INFO]    \- commons-io:commons-io:jar:2.2:compile

3. CommonsMultipartResolver

Create a multipartResolver bean.

SpringWebMvcConfig.java

package com.mkyong;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
@ComponentScan({"com.mkyong"})
public class SpringWebMvcConfig extends WebMvcConfigurerAdapter {

    private int maxUploadSizeInMb = 5 * 1024 * 1024; // 5 MB

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Bean
    public CommonsMultipartResolver multipartResolver() {

        CommonsMultipartResolver cmr = new CommonsMultipartResolver();
        cmr.setMaxUploadSize(maxUploadSizeInMb * 2);
        cmr.setMaxUploadSizePerFile(maxUploadSizeInMb); //bytes
        return cmr;

    }

}

Done, the file upload configuration is set up properly.

4. ServletInitializer

A standard Servlet initializer, nothing special here.

MyWebInitializer.java

package com.mkyong;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MyWebInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringWebMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

}

5. Spring Controller

5.1 The uploaded files will map to MultipartFile

UploadController.java

package com.mkyong.controller;

import com.mkyong.model.UploadForm;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.StringJoiner;

@Controller
public class UploadController {

	// save uploaded file to this folder
    private static String UPLOADED_FOLDER = "F://temp//";

    @GetMapping("/")
    public String index() {
        return "upload";
    }

    @PostMapping("/upload")
    public String multiFileUpload(@ModelAttribute UploadForm form,
                                  RedirectAttributes redirectAttributes) {

        StringJoiner sj = new StringJoiner(" , ");

        for (MultipartFile file : form.getFiles()) {

            if (file.isEmpty()) {
                continue; //next pls
            }

            try {

                byte[] bytes = file.getBytes();
                Path path = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
                Files.write(path, bytes);

                sj.add(file.getOriginalFilename());

            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        String uploadedFileName = sj.toString();
        if (StringUtils.isEmpty(uploadedFileName)) {
            redirectAttributes.addFlashAttribute("message", "Please select a file to upload");
        } else {
            redirectAttributes.addFlashAttribute("message", "You successfully uploaded '" + uploadedFileName + "'");
        }

        return "redirect:/uploadStatus";

    }

    @GetMapping("/uploadStatus")
    public String uploadStatus() {
        return "uploadStatus";
    }

}

5.2 Maps HTML values into this model.

UploadForm.java

package com.mkyong.model;

import org.springframework.web.multipart.MultipartFile;

public class UploadForm {

    MultipartFile[] files;

    public MultipartFile[] getFiles() {
        return files;
    }

    public void setFiles(MultipartFile[] files) {
        this.files = files;
    }

}

6. Spring JSP Views

upload.jsp

<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<html>

<body>
<h1>Spring MVC multi files upload example</h1>

<form:form method="POST" action="${pageContext.request.contextPath}/uploadMulti" 
           modelAttribute="uploadForm" enctype="multipart/form-data">
    
    <input type="file" name="files" /><br/>
    <input type="file" name="files" /><br/>
    <input type="file" name="files" /><br/><br/>
    <input type="submit" value="Submit" />
    
</form:form>

</body>
</html>
uploadStatus.jsp

<html>
<body>
<h1>Upload Status</h1>
<h2>Message : ${message}</h2>
</body>
</html>

7. Max upload size exceeded

To handle the max upload size exceeded exception, declares a @ControllerAdvice and catch the MaxUploadSizeExceededException

GlobalExceptionHandler.jsp

package com.mkyong.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@ControllerAdvice
public class GlobalExceptionHandler {

    //commons-fileupload
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public String handleError2(MaxUploadSizeExceededException e, RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
        return "redirect:/uploadStatus";

    }

}

8. DEMO

8.1 Get the source code below and test with the embedded Jetty server mvn jetty:run. Context is /spring4upload

Terminal

project $ mvn jetty:run
//...
[INFO] Started o.e.j.m.p.JettyWebAppContext@341672e{/spring4upload,
	file:/SpringMVCUploadExample/src/main/webapp/,AVAILABLE}{file:/SpringMVCUploadExample/src/main/webapp/}
[WARNING] !RequestLog
[INFO] Started ServerConnector@3ba1308d{HTTP/1.1}{0.0.0.0:8080}
[INFO] Started @3743ms
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.

8.2 Access http://localhost:8080/spring4upload , select a few of files and upload it.

spring-multi-file-upload-example1

8.3 Result.

spring-multi-file-upload-example2

8.3 If you upload a total file size bigger than 10mb, this exception page will be displayed.

spring-multi-file-upload-example-max-size
Connection Reset
If you deployed the web app to Tomcat, and unable to catch the file size exceeded exception, this may cause by the Tomcat maxSwallowSize setting. Read this – Spring file upload and connection reset issue

8. Download Source Code

References

  1. Spring Uploading Files
  2. Spring’s multipart (file upload) support
  3. Spring MVC file upload example
  4. Spring file upload and connection reset issue

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

avatar
2 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
hugasuraj Recent comment authors
newest oldest most voted
huga
Guest
huga

How we can update saved files?

suraj
Guest
suraj

JSP files are missing. Please add