web-dev-qa-db-ja.com

Spring Restテンプレート+ Spring Web MVCを使用したマルチパートファイルのアップロード

次のコードでRestTemplateを使用してファイルをアップロードしようとしています。

   MultiValueMap<String, Object> multipartMap = new LinkedMultiValueMap<>();
   multipartMap.add("file", new ClassPathResource(file));

   HttpHeaders headers = new HttpHeaders();
   headers.setContentType(new MediaType("multipart", "form-data"));

   HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(multipartMap, headers);

   System.out.println("Request for File Upload : " + request);

   ResponseEntity<byte[]> result = template.get().exchange(
                    contextPath.get() + path, HttpMethod.POST, request,
                    byte[].class);

MultipartResolver Beanがあり、コントローラーコードは

@RequestMapping(value = "/{id}/image", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
@Transactional(rollbackFor = Exception.class)
public byte[] setImage(@PathVariable("id") Long userId,
        @RequestParam("file") MultipartFile file) throws IOException {
    // Upload logic
}

そして、私は次の例外を取得します

 org.springframework.web.bind.MissingServletRequestParameterException: Required MultipartFile parameter 'file' is not present
        at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.Java:255) ~[spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.Java:95) ~[spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.Java:79) ~[spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.Java:157) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.Java:124) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.Java:104) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.Java:749) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.Java:689) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.Java:83) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.Java:938) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.Java:870) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:961) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.Java:863) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.Java:646) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:837) [spring-webmvc-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.Java:727) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:303) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:208) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.springframework.web.filter.ShallowEtagHeaderFilter.doFilterInternal(ShallowEtagHeaderFilter.Java:80) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:241) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:208) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.Java:77) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:241) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:208) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.Java:67) [log4j-web-2.0.2.jar:2.0.2]
        at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:241) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:208) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:330) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.Java:118) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.Java:84) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.Java:113) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.Java:103) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.Java:113) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.Java:154) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.Java:45) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at m.m.m.AbstractPreAuthenticatedProcessingFilter.doFilter(UapAbstractPreAuthenticatedProcessingFilter.Java:109) [classes/:?]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.Java:50) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.Java:87) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.Java:342) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.Java:192) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.Java:160) [spring-security-web-3.2.5.RELEASE.jar:3.2.5.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.Java:344) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.Java:261) [spring-web-4.0.6.RELEASE.jar:4.0.6.RELEASE]
        at org.Apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.Java:241) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.Java:208) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.Java:220) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.StandardContextValve.invoke(StandardContextValve.Java:122) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.StandardHostValve.invoke(StandardHostValve.Java:171) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.Java:102) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.Java:950) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.Java:116) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.Java:408) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.Java:1040) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.Java:607) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at org.Apache.Tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.Java:314) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1145) [?:1.7.0_67]
        at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:615) [?:1.7.0_67]
        at org.Apache.Tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.Java:61) [Tomcat-embed-core-7.0.54.jar:7.0.54]
        at Java.lang.Thread.run(Thread.Java:745) [?:1.7.0_67]
33
shazin

マルチパートファイルアップロードは、RestTemplateを使用してアップロードするようにコードを変更した後に機能しました

LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("file", new ClassPathResource(file));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);

HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new    HttpEntity<LinkedMultiValueMap<String, Object>>(
                    map, headers);
ResponseEntity<String> result = template.get().exchange(
                    contextPath.get() + path, HttpMethod.POST, requestEntity,
                    String.class);

そして、web.xmlにMultipartFilterを追加します

    <filter>
        <filter-name>multipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>multipartFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
46
shazin

ここに私の実例があります

@RequestMapping(value = "/api/v1/files/upload", method =RequestMethod.POST)
public ResponseEntity<?> upload(@RequestParam("files") MultipartFile[] files) {
    LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
    List<String> tempFileNames = new ArrayList<>();
    String tempFileName;
    FileOutputStream fo;

    try {
        for (MultipartFile file : files) {
            tempFileName = "/tmp/" + file.getOriginalFilename();
            tempFileNames.add(tempFileName);
            fo = new FileOutputStream(tempFileName);
            fo.write(file.getBytes());
            fo.close();
            map.add("files", new FileSystemResource(tempFileName));
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
        String response = restTemplate.postForObject(uploadFilesUrl, requestEntity, String.class);

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

    for (String fileName : tempFileNames) {
        File f = new File(fileName);
        f.delete();
    }
    return new ResponseEntity<Object>(HttpStatus.OK);
}
15

ほとんどのユースケースでは、Spring MVCはすでにマルチパートリクエストの処理を行っているため、web.xmlにMultipartFilterを登録するのは正しくありません。フィルターのjavadocにも記述されています。

サーバー側で、アプリコンテキストでmultipartResolver Beanを定義します。

@Bean
public CommonsMultipartResolver multipartResolver(){
    CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
    commonsMultipartResolver.setDefaultEncoding("utf-8");
    commonsMultipartResolver.setMaxUploadSize(50000000);
    return commonsMultipartResolver;
}

クライアント側で、Spring RestTemplate APIで使用するリクエストを準備する方法は次のとおりです。

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    LinkedMultiValueMap<String, String> pdfHeaderMap = new LinkedMultiValueMap<>();
    pdfHeaderMap.add("Content-disposition", "form-data; name=filex; filename=" + file.getOriginalFilename());
    pdfHeaderMap.add("Content-type", "application/pdf");
    HttpEntity<byte[]> doc = new HttpEntity<byte[]>(file.getBytes(), pdfHeaderMap);

    LinkedMultiValueMap<String, Object> multipartReqMap = new LinkedMultiValueMap<>();
    multipartReqMap.add("filex", doc);

    HttpEntity<LinkedMultiValueMap<String, Object>> reqEntity = new HttpEntity<>(multipartReqMap, headers);
    ResponseEntity<MyResponse> resE = restTemplate.exchange(uri, HttpMethod.POST, reqEntity, MyResponse.class);

重要なことは、正確な大文字小文字を使用してContent-dispositionヘッダーを提供し、名前とファイル名の指定子を追加することです。そうしないと、マルチパートリゾルバーによってパーツが破棄されます。

その後、コントローラーメソッドは、アップロードされたファイルを次の引数で処理できます。

@RequestParam("filex") MultipartFile file

お役に立てれば。

10
PA Lemire

次のようなエラーが発生している場合:

I/O error on POST request for "anothermachine:31112/url/path";: class path 
resource [fileName.csv] cannot be resolved to URL because it does not exist.

を使用して解決できます

LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("file", new FileSystemResource(file));

ファイルがクラスパスに存在せず、絶対パスが必要な場合。

6
RAX

もっと感じに基づいていますが、これはコンテキスト設定でBeanを宣言するのを忘れた場合に取得するエラーですので、追加してみてください

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="10000000"/>
</bean>
1
Master Slave