Spring-Boot + Tomcat 8.0.3 FileUpload error Unable to process parts as no multi-part configuration has been provided

StackOverflow https://stackoverflow.com/questions/23604357

Question

I have a working spring-boot mvc application where i am trying integrate a file upload option. As per manual I have configured the following template:

/src/main/resource/templates/fileUpload.html

<!DOCTYPE html>
<html layout:decorator="layout">

<head> 
<title>syncServer File upload example</title>
</head>
  <body>
    <div layout:fragment="content">

    <div >
        <form th:action="@{/upload}" enctype="multipart/form-data" th:method="post">
            <fieldset>
                     <legend>File to upload:</legend>
                     <input type="file" name="myFile" />
                     <label>Name :</label>
                     <input type="text" name="myName"/>
                     <input type="submit" />
            </fieldset>
    </form>
    </div>

    </div>

  </body>
</html>

when i login and then call http:/127.0.0.5:8080/upload the form displays but when i click the submit button i got. the error message

HTTP Status 403 - Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'.

The i went and disable csrf in order to see if the form will simply work if the cross site request forgery is disabled (".csrf().disable()"). i.e.

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
//          .headers().disable()
            .authorizeRequests()
                .antMatchers("/resources/css/**","/register", "/resources/img/**" , "/resources/js/**", "/resources/pdf/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

When i tried to upload a another file during the csrf was disabled i got another error i.e.:

HTTP Status 500 - Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided

it says "no multi-part configuration has been provided" but i do have configuration file that configures the multipartConfigElement i.e.

@Configuration
public class FileUpload {
    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultiPartConfigFactory factory = new MultiPartConfigFactory();
        factory.setMaxFileSize("600KB");
        factory.setMaxRequestSize("600KB");
        return factory.createMultipartConfig();
    }
}

I have also the @ComponentScan anotation and the @EnableAutoConfiguration above my Application class i.e.:

@ComponentScan(basePackages = "org.syncServer.*")
@Configuration
@EnableWebSocketMessageBroker
@EnableAutoConfiguration
public class Application extends SpringBootServletInitializer {
...
}

When the application starts i am listing all the beans that are managed by spring and i do see that the bean "multipartResolver" is being initiated.

Controller associated with the fileUpload.html:

@Controller
public class FileUploadController {


    @RequestMapping(value="/upload", method=RequestMethod.GET)
    public String provideUploadInfo() {
        return "fileUpload";
    }


    @RequestMapping(value="/upload", method=RequestMethod.POST)
    public @ResponseBody String handleFileUpload(@RequestParam("myName") String myName, 
            @RequestParam("myFile") MultipartFile myFile){
        if (!myFile.isEmpty()) {
            try {
                byte[] bytes = myFile.getBytes();
                BufferedOutputStream stream = 
                        new BufferedOutputStream(new FileOutputStream(new File(myName + "-uploaded")));
                stream.write(bytes);
                stream.close();
                return "Successfully uploaded " + myName + " into " + myName + "-uploaded !";
            } catch (Exception e) {
                return "Failed to upload " + myName + " => " + e.getMessage();
            }
        } else {
            return "Failed to upload " + myName + " empty file.";
        }
    }

}

When i set a breakpoint at the get method i see that the method is hit and it returns the fileUpload.html view. However the post method is not hit at all.

I do not know if that is relevant but i have set in Project-Properties-Deployment assembly - /src/main/resources as "/" this is the only entry i have in my Deployment Assembly configuration.

I have read before that there was a bug in old spring-boot RC version with the file uploads. I am currently using the spring-boot 1.0.2.RELEASE. My pom file

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.0.2.RELEASE</version>
    </parent>


    <groupId>sync</groupId>
    <artifactId>syncServer</artifactId>
    <name>syncServer</name>

    <!-- When removing the parent project configuration you have to explicitly set the dependencies version -->
    <dependencies>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate}</version>
        </dependency>
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.1-901.jdbc4</version>
        </dependency>

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
        </dependency>



        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat8.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>8.0.3</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>

        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity3</artifactId>
        </dependency>
        <dependency>
            <groupId>nz.net.ultraq.thymeleaf</groupId>
            <artifactId>thymeleaf-layout-dialect</artifactId>
            <version>1.2.1</version>
        </dependency>
    </dependencies>

    <version>1.0-SNAPSHOT</version>

    <build>
        <defaultGoal>test</defaultGoal>
        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>

                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
            </plugin>


        </plugins>



    </build>



  <repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>http://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>

        <pluginRepository>
            <id>spring-milestones</id>
            <url>http://repo.spring.io/milestone</url>
        </pluginRepository>


    </pluginRepositories>

    <properties>
        <java-version>1.7</java-version>
        <spring-version>4.0.2.RELEASE</spring-version>
        <org.aspectj-version>1.6.10</org.aspectj-version>
        <org.slf4j-version>1.7.6</org.slf4j-version>
        <start-class>org.syncServer.core.Application</start-class>
        <springBootVersion>1.0.2.RELEASE</springBootVersion>
        <tomcat8.version>8.0.3</tomcat8.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <hibernate>4.3.1.Final</hibernate>
    </properties>



</project>

Addition here is the full html code as requested in the comments:

<!DOCTYPE html>


<html>
  <head>
<title>Task List - syncServer File upload example</title>

    <link rel="stylesheet" type="text/css" media="all" href="/css/syncServer.css" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script src="/js/jquery.js"></script>



</head>

  <body>
     <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">My project</a>
        </div>
        <div class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/">Home</a></li>
            <li><a href="/message">OpenSource</a></li>
            <li><a href="/task">Documents</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
<!--             <li th:if="${#authorization.expression('!isAuthenticated()')}"> -->
<!--               <a href="/signin" th:href="@{/signin}">Sign in</a> -->
<!--             </li> -->
<!--             <li th:if="${#authorization.expression('isAuthenticated()')}"> -->
<!--               <a href="/logout" th:href="@{/logout}">Logout</a> -->
<!--             </li> -->
          </ul>
        </div>
      </div>
    </div>
    <div class="container">
      <div>

    <div>
        <form enctype="multipart/form-data" method="post" action="/upload">
            <fieldset>
                     <legend>File to upload:</legend>
                     <input type="file" name="myFile" />
                     <label>Name :</label>
                     <input type="text" name="myName" />
                     <input type="submit" />
            </fieldset>
    </form>
    </div>

    </div>
      <div>
      © 2014
      <br />
      Autumn, Winter, Summer , Spring is always inside.
    </div>
    </div>
  </body>
</html>

Addition 2 link to the working maven project showing the problem

https://github.com/TheDictator/sArchitecture

in order to reproduce login with:

user name: admin2 password: #passworD

and go to

http://127.0.0.5:8080/upload

the error message described above is see.

Addition 3

My particular problem is fixed when i removed the bean that i have previously declared before the entry point mail in the application class i.e.

@Bean
public ServletRegistrationBean dispatcherRegistration() {

    System.out.println("SERVLET REGISTRATION");
    ServletRegistrationBean registration = new ServletRegistrationBean(
            dispatcherServlet());

    System.out.println("SERVLET REGISTERED NAME is: "
            + registration.getServletName().toString());
    registration.addUrlMappings("/");

    return registration;
}

I have not answered this question my self as i do not yet understand why this peace of code is messing up the upload functionality.

Was it helpful?

Solution

You register the DispatcherServlet yourself (instead of letting Spring Boot do it) so you have to take care to add the multipart configuration for it. Spring Boot only adds multipart configuration to the ServletRegistrationBeans that it creates itself (see here) so if you create your own you have to take care of it. Hopefully the API of ServletRegistrationBean is clear enough that this is implied, if not obvious

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top