5

I'm trying to upload a multipart file using Ajax, Spring MVC 3.2.0, Tomcat 8.0.9, but can't get it work. I read a lot of blogs and similar posting here on stackoverflow (Spring upload file problems, MultipartConfig with Servlet 3.0 on Spring MVC, …) which seem to have similar causes but couldn't figure out how to solve it. The weird thing is that the upload works when the file is smaller than 1MB, but when ever the recorded video exceeds that size, the following error is raised:

org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. null
org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:163)
org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:110)

In the following you can see all the configurations I made:

  1. The AJAX POST-Request:

    var videoBlob = e.data;
    var pathArray = window.location.pathname.split( '/' );
    var userID;
    
    for (i = 0; i < pathArray.length; i++) {
        if (pathArray[i].toString() == "edit"){
            userID = pathArray[i+1];
        }
    }
    
    var fd = new FormData();
    fd.append('fname', 'video');
    fd.append('data', videoBlob);
    
    $.ajax({
        url: '/user/edit/uploadVideo/' + userID,
        data: fd,
        processData: false,
        contentType: false,
        type: 'POST',
        success: function(data)
        {
            $('#result').html(data + "uploaded by FormData!");
        }
    });
    
  2. The web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:root-context.xml</param-value>
    </context-param>
    
    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>common</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <filter>
        <display-name>springMultipartFilter</display-name>
        <filter-name>springMultipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>springMultipartFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
  3. The servlet-context.xml

    <mvc:annotation-driven />
    <mvc:resources mapping="/**" location="/resources/" />
    
    <context:component-scan base-package="de.talentwuerfel"/>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/" />
    <property name="suffix" value=".jsp" />
    </bean>
    
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/schema"/>
    <property name="username" value="root"/>
    <property name="password" value=""/>
    <property name="validationQuery" value="SELECT 1"/>
    </bean>
    
    <bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan">
        <array>
            <value>de.talentwuerfel</value>
        </array>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
    </bean>
    
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>
    
    <bean id="persistenceExceptionTranslationPostProcessor"
      class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
  4. The root-context.xml where I defined the MultipartResolver

    <bean id="filterMultipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize"   value="100000000"/>
        <property name="maxInMemorySize" value="4096"/>
    </bean>
    
  5. The Java-Controller

    @RequestMapping(value = "/edit/uploadVideo/{id}", method = RequestMethod.POST)
    public @ResponseBody String uploadVideo(@PathVariable long id, MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
          //.... file handling
    }
    

How can I solve this problem?

EDIT:

I tried the suggested approach and used the Servlet implementation to manage my video-file upload. The following adjustments have been made, but it's still resulting in a similar error:

  1. Adjusted @Controller:

    @RequestMapping(value = "/edit/uploadVideo/{id}", method = RequestMethod.POST)
    public String uploadVideo(@PathVariable long id, @RequestParam("data") Part file) {
    
    //...
    }
    
  2. The root-controller has been deleted and I added the multipartResolver to the servlet-context.xml

    <bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
    </bean>
    
  3. The tag was in the web.xml has been extended by the following Multipart-Configuration:

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <multipart-config>
            <location>/tmp</location>
            <max-file-size>20848820</max-file-size>
            <max-request-size>418018841</max-request-size>
            <file-size-threshold>1048576</file-size-threshold>
        </multipart-config>
    </servlet>
    

However, I'm still getting an exception and can't upload a blob file larger than 1MB:

Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. null
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:927)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:822)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:796)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725) 

I implemented a similar file upload where a single file was simply picked and it totally worked to send large files while using the same configuration. So I believe it has rather something to do with the Ajax POST or the attached blob file?!

Community
  • 1
  • 1
vanNiewoehner
  • 51
  • 1
  • 1
  • 4
  • Have you trieg modifying the maxUploadSize parameter (in your filterMultipartResolver) to a value superior to 100000000 ? Like 200000000 for example. - EDIT : sorry, I thought it was related but the size in in byte, so you would have a max file size of 100MB which is enough... http://docs.oracle.com/javaee/6/tutorial/doc/gmhal.html – m4rtin Aug 17 '14 at 14:54
  • Tried that already but didn't help. Here is the request header that is created by the AJAX-request: Accept */* Accept-Encoding gzip, deflate Accept-Language en-US,en;q=0.5 Content-Length 1422427 Content-Type multipart/form-data; boundary=---------------------------16605868027849048022050447340 Cookie JSESSIONID=D3611B0B660302FF7A6A2D2BD57ADEDD Host localhost:8080 Referer http://localhost:8080/user/edit/2 User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) Gecko/20100101 Firefox/31.0 X-Requested-With XMLHttpRequest – vanNiewoehner Aug 17 '14 at 14:57

2 Answers2

1

you can add this in you application to active Servlet3.0 MultiParsing:

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

or do it in XML.

  • If you're using Spring Boot 1.5.x then the class name is `org.springframework.boot.web.servlet.MultipartConfigFactory` (attention to lowercase "P") – shimatai Apr 08 '18 at 05:08
0

Not really an answer to your exact question, just my 2 cents. There are basically 2 ways of uploading files with the help of Spring MVC :

  • using Jakarta Commons FileUpload, the only way (aside from implementing one yourself) before the appearance of the servlet 3.0 API
  • using the Servlet implementation of your server (only if servlet impl version >= 3.0)

Since you are using Tomcat 8.0.9, the Servet 3.0 option is available to you which I definitely recommend since it doesn't introduce yet another external dependency in your project. Also, since it follows the Servlet 3.0 spec, the configuration of such upload mechanism is now java standard which is nice in case you decide to move from Spring MVC to another MVC framework (your configuration values would remain the same).

In case you can't figure out your IOFileUploadException, I think you should give it a try.

m4rtin
  • 2,445
  • 22
  • 34
  • Thanks for the tip, servlet impl version is 3.0 and I'll give it a try! – vanNiewoehner Aug 17 '14 at 18:11
  • Let us know how it goes. I suggested this solution also because I worked with the Servlet 3.0 way more than with the Commons FileUpload one so I could be of more help if you were to encounter any issue using the Servlet 3.0 way. – m4rtin Aug 17 '14 at 18:20
  • I tried the Servlet 3.0 implementation but still can't send the desired files. Please have a look at the edited question where I attached the new code/configuration. – vanNiewoehner Aug 18 '14 at 15:04
  • I find it weird that you're still getting a stack containing "org.apache.tomcat.util.http.fileupload", did you properly removed the Commons FileUpload dependency from your project ? If yes, it must indeed be something about the AJAX request. – m4rtin Aug 18 '14 at 15:15
  • Also, could you please try with org.springframework.web.multipart.MultipartFile in your controller's method signature (this -> @RequestParam("data") MultipartFile file instead of this -> @RequestParam("data") Part file). And even try with an empty signature... because apart from the request being "corrupted" from a java standpoint, I don't understand why the server-side request processing fail *this* early (FrameworkServlet.processRequest(FrameworkServlet.java:927)). – m4rtin Aug 18 '14 at 15:32
  • Also please remove any servlet filter dealing with this upload. It appears that there is a problem with Spring MVC dealing with multipart request processing in filters (as per this answer : http://stackoverflow.com/a/23224378/3403161 where Rob Winch himself advocates the Commons FileUpload way to workaround this bug). If you absolutely need to deal with this upload in a filter, I'm afraid I gave you wrong directions :| But hopefully it won't be the case... – m4rtin Aug 18 '14 at 15:47
  • The Commons FileUpload dependency is certainly removed from the pom.xml and the corresponding .jar-file does no longer exist in the project. I also changed the signature, but no success. There are no active filters in my web.xml, only the tags (including ) and . As I said, the Ajax request works with small video/webm files, but as soon as the the size of the video exceeds the 1000000kb, it crashes. I read the post by Rob Winch two days ago and followed the suggested solution to use the Commons FileUpload, but it didn't help in my case. – vanNiewoehner Aug 18 '14 at 16:16
  • I'm quite new to the whole topic, but can it be the case that the video file itself is not saved properly and hence, causes the raised exceptions during the request? – vanNiewoehner Aug 18 '14 at 16:17
  • Honestly, that's about all there is left as a possibility to explain your problem. Plus you said the upload worked for large files that were not videos so I think you're right. Still it really bugs me that it would be a size matter, the file should be encoded the same way whether it be 100kb or 1000000kb so why does it crash above that limit is beyond my comprehension. Sorry we didn't manage to resolve your problem, maybe you'll have better luck searching around that video size matter (for example, could it be a size limit fixed by tomcat ?) Don't forget to post your answer if you find one ;) – m4rtin Aug 18 '14 at 16:28
  • Yes, I implemented a "regular" file-upload with Ajax and selected files above 5MB which was no problem at all. However, if I record a video via html5 and append the resulting blob file (content-type = video/webm) manually to a FormData object, the file transfer doesn't want to work for files larger than 1MB. All other videos are correctly stored on the server. However, I'll continue my search, there must be a solution ;) Thank you for your effort! – vanNiewoehner Aug 18 '14 at 16:37