Spring rest MultiPart 文件上传 Java 配置没有 Spring 启动

Spring rest MultiPart file upload with Java configuration without Spring boot

我正在尝试实施一个休息网络服务,该服务使用 MultipartFile 上传文件,使用 Spring,配置 java。我不使用 Spring 引导,我的 class 路径中有 commons-fileupload 库。

我读了 Spring 文档说:

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

因此我将这个 bean 添加到我的 AppConfig class:

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

并用 MultipartConfig 注释 class:

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

但是我在调​​用服务时遇到了这个异常:

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)

如果我使用 CommonsMultipartResolver 而不是 StandardServletMultipartResolver,我会得到同样的错误。

这是我初始化应用程序的方式:

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, "/*");

    }
}

我也试过添加 MultipartFilter 但没有成功。

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

有这个必要吗?我究竟做错了什么?我想我阅读了整个互联网来寻找解决方案,但他们都使用 spring 引导,MultipartConfigElementMultipartConfigFactory。也许问题是我使用服务的方式?

这是我的控制器方法:

@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)

这就是我的消费方式:

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);

请帮忙我不知道还能做什么。

我想你可能想尝试这样的事情:

        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;
        }
    }

配置 Spring 项目以使用 java 配置处理文件上传的正确方法是: 如果你想用 Commons FileUpload 库配置它,你只需要在你的配置 class 中包含这个 bean 并在你的 classpath

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

如果你想使用 Servlet 3.0 配置项目,正如@AlieneilA 所说,你必须在调度程序 servlet 中设置 MultipartConfig 元素:

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

并将此 bean 包含在配置中 class(在我的例子中是 AppConfig):

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

我将文件插入 LinkedMultiValueMap 的方式有误。我不得不使用 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);

或此答案建议的 MockMultipartFile

在这种情况下,包括 spring 模拟作为依赖项:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-mock</artifactId>
    <version>2.0.8</version>
</dependency>

如果有人扩展 AbstractAnnotationConfigDispatcherServletInitializer class 用于 Web 应用程序初始化配置,下面的简单配置将启用 MultiPart 功能 -

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);
    }

}