4

I am trying to implement a rest web service that uses MultipartFile to upload a file using Spring, with java configuration. I do not use Spring Boot and I have commons-fileupload library in my classpath.

I read Spring documentation that says:

you need to mark the DispatcherServlet with a "multipart-config" section in web.xml, or with a javax.servlet.MultipartConfigElement in programmatic Servlet registration, or in case of a custom Servlet class possibly with a javax.servlet.annotation.MultipartConfig annotation on your Servlet class ... Once Servlet 3.0 multipart parsing has been enabled in one of the above mentioned ways you can add the StandardServletMultipartResolver to your Spring configuration

Hence I added this bean to my AppConfig class:

 @Bean
 public StandardServletMultipartResolver multipartResolver() {
    return new StandardServletMultipartResolver();
 }

and annotated the class with MultipartConfig:

@EnableWebMvc
@MultipartConfig(maxFileSize = 5120)
public class AppConfig extends WebMvcConfigurerAdapter{
 ...
}

but I get this exception when I call the service:

Caused by: org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.UnsupportedOperationException: SRVE8020E: Servlet does not accept multipart request
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:85)
    at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:76)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:112)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    at [internal classes]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:207)
    ... 1 more
Caused by: java.lang.UnsupportedOperationException: SRVE8020E: Servlet does not accept multipart request
    at com.ibm.ws.webcontainer.srt.SRTServletRequest.prepareMultipart(SRTServletRequest.java:3657)
    at [internal classes]
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:92)

If I use CommonsMultipartResolver instead of StandardServletMultipartResolver I get the same error.

This is how I initialize my application:

public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);
        context.setServletContext(servletContext);

        servletContext.addListener(new ContextLoaderListener(context));

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcherServlet", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");


        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);

        EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
        FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("CharacterEncodingFilter", characterEncodingFilter);
        characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");

    }
}

I also tried add a MultipartFilter but with no luck.

MultipartFilter multipartFilter = new MultipartFilter();
FilterRegistration.Dynamic multipart = servletContext.addFilter("multipartFilter", multipartFilter);
    multipart.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");

Is this necessary? What am I doing wrong? I think I read the whole internet searching for a solution but they all use spring boot with MultipartConfigElement and MultipartConfigFactory. Maybe the problem is the way I consume the service?

This is my controller method:

@RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data" )
public Long uploadAttachment(@RequestParam("cn") String callerName, @RequestParam("cs") String callerService, @RequestParam("file")  MultipartFile file)

and this is how i consume it:

File file = new File("C:\\Users\\cte0289\\Documents\\Email\\document.docx");
RestTemplate rest = new RestTemplate();
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();

map.add("cn", callerName);
map.add("cs", callerService);
map.add("file", file);            
Long response = rest.postForObject(url + "/upload", map, Long.class);

Please help I don't know what else to do.

6
  • Adding @MultipartConfig is pretty much useless as that should be on a servlet. When using the dispatcher servlet you have to configure it with the MultipartConfigElement on the registration object you get when registering a servlet. Or switch to using commons file upload instead. So as long as you haven't configured the servlet correct the servlet 3.0 fie upload will not work (how many filters you try to put in front of that). Commented Jan 31, 2017 at 11:43
  • i tried adding this line in AppInitializer class dispatcher.setMultipartConfig(new MultipartConfigElement("C:/tmp", 1024*1024*5, 1024*1024*5*5, 1024*1024)); to set the MultipartConfigElement but i still get the error. Is this wrong? Answering you question i only need CharacterEncodingFilter Commented Jan 31, 2017 at 12:23
  • @M.Deinum do you know if the way i am using to consume the service is right? I tried both approaches, with commons file upload and servlet 3.0 but i am not able to make it work. Commented Jan 31, 2017 at 16:07
  • I suggest you start with a clean slate instead of trying to hammer it in. Start by removing the filter. Commented Feb 1, 2017 at 6:38
  • In case anyone else is wondering where the quoted Spring doc is to be found, it is here in this JavaDoc: docs.spring.io/spring-framework/docs/5.3.19/javadoc-api/org/… Commented Apr 25, 2024 at 11:10

3 Answers 3

2

The correct way to configure Spring project to handle file upload with java configuration is this: If you want to configure it with Commons FileUpload library you have only to include this bean in your Configuration class and add the jar in your classpath

@Bean
public CommonsMultipartResolver multipartResolver(){
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setMaxUploadSize(5242880); // set the size limit
    return resolver;
}

if you want to configure the project with Servlet 3.0, as @AlieneilA said you have to set the MultipartConfig element in dispatcher servlet:

dispatcher.setMultipartConfig(new MultipartConfigElement("C:/tmp", 1024*1024*5, 1024*1024*5*5, 1024*1024));

and include this bean in configuration class (AppConfig in my case):

@Bean
public StandardServletMultipartResolver multipartResolver() {
   return new StandardServletMultipartResolver();
}

I was wrong in the way i inserted the file into the LinkedMultiValueMap. I had to use a FileSystemResource:

File file = new File("C:\\document.doc");
RestTemplate rest = new RestTemplate();
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();

map.add("param1", param1);
map.add("param2", param2);
map.add("file", new FileSystemResource(file));            
Long response = rest.postForObject(url, map, Long.class);

or a MockMultipartFile as suggested by this answer: https://stackoverflow.com/a/38270420/6503002

In this case include spring mock as dependency:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-mock</artifactId>
    <version>2.0.8</version>
</dependency>
Sign up to request clarification or add additional context in comments.

Comments

1

I think you might want to try something like this:

        public class AppInitializer implements WebApplicationInitializer {

        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(AppConfig.class);
            context.setServletContext(servletContext);

            servletContext.addListener(new ContextLoaderListener(context));

            ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcherServlet", new DispatcherServlet(context));
            dispatcher.setLoadOnStartup(1);
            dispatcher.addMapping("/");
dispatcher.setMultipartConfig(getMultipartConfigElement());


            CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
            characterEncodingFilter.setEncoding("UTF-8");
            characterEncodingFilter.setForceEncoding(true);

            EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
            FilterRegistration.Dynamic characterEncoding = servletContext.addFilter("CharacterEncodingFilter", characterEncodingFilter);
            characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");

        }

    private MultipartConfigElement getMultipartConfigElement(){
            MultipartConfigElement multipartConfigElement = new MultipartConfigElement("C:/tmp", 1024*1024*5, 1024*1024*5*5, 1024*1024);
            return multipartConfigElement;
        }
    }

4 Comments

I tried this solution but i still get the same error. Thanks anyway!
then I suggest you look at how you are uploading the file. I see that you have declared: File file = new File(..) Maybe it would be nice to make a class for a file (a filebucket) and should you not be trying to upload a MultipartFile ?
You were right, i used the wrong method to upload the file. Thank you for the suggestion. +1
Happy to be of help to you!
0

In case anyone is extending AbstractAnnotationConfigDispatcherServletInitializer class for web application initialization configuration, below simple configuration will enable MultiPart Feature -

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;

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

public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

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

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

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

    @Override
    protected void customizeRegistration(Dynamic registration) {

        MultipartConfigElement multiPart = new MultipartConfigElement("C:/temp/",
                1024 * 1024 * 5, 1024 * 1024 * 10, 1024 * 1024 * 3);

        registration.setMultipartConfig(multiPart);
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.