使用RestTemplate请求将多部分文件作为POST参数发送

我正在使用Spring 3和RestTemplate。 我基本上有两个应用程序,其中一个必须将值发布到另一个应用程序。 通过rest模板。

当要发布的值是string,它是完美的工作,但是当我必须发布混合和复杂的参数(如MultipartFiles)我得到一个转换器exception。

举个例子,我有这个:

App1 – PostController:

@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute UploadDTO pUploadDTO, BindingResult pResult) throws URISyntaxException, IOException { URI uri = new URI("http://localhost:8080/app2/file/receiver"); MultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>(); mvm.add("param1", "TestParameter"); mvm.add("file", pUploadDTO.getFile()); // MultipartFile Map result = restTemplate.postForObject(uri, mvm, Map.class); return "redirect:postupload"; } 

另一方面,我有另一个Web应用程序(App2)从App1接收参数。

App2 – ReceiverController

 @RequestMapping(value = "/receiver", method = { RequestMethod.POST }) public String processUploadFile( @RequestParam(value = "param1") String param1, @RequestParam(value = "file") MultipartFile file) { if (file == null) { System.out.println("Shit!... is null"); } else { System.out.println("Yes!... work done!"); } return "redirect:postupload"; } 

我的application-context.xml:

 <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /> <bean class="org.springframework.http.converter.FormHttpMessageConverter" /> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /> </list> </property> </bean> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize"> <value>104857600</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> </bean> 

这里是当我做RestTemplate的postForObject的时候得到的exception的堆栈…

 org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.web.multipart.commons.CommonsMultipartFile] at org.springframework.http.converter.FormHttpMessageConverter.writePart(FormHttpMessageConverter.java:292) at org.springframework.http.converter.FormHttpMessageConverter.writeParts(FormHttpMessageConverter.java:252) at org.springframework.http.converter.FormHttpMessageConverter.writeMultipart(FormHttpMessageConverter.java:242) at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:194) at org.springframework.http.converter.FormHttpMessageConverter.write(FormHttpMessageConverter.java:1) at org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:588) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:436) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:415) at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:294) at com.yoostar.admintool.web.UploadTestController.create(UploadTestController.java:86) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:175) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:421) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:409) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:774) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:619) 

所以我的问题是:

  1. 是否可以使用POST通过RestTemplate发送MultipartFile?
  2. 有一些特定的转换器,我不得不用来发送这种types的对象? 我的意思是有一些MultipartFileHttpMessageConverter在我的configuration中使用?

一种解决这个问题的方法就是使用一个ByteArrayResource,这样你可以在你的文章中发送一个字节数组(这段代码可以在Spring 3.2.3中使用),而不需要使用FileSystemResource。

 MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>(); final String filename="somefile.txt"; map.add("name", filename); map.add("filename", filename); ByteArrayResource contentsAsResource = new ByteArrayResource(content.getBytes("UTF-8")){ @Override public String getFilename(){ return filename; } }; map.add("file", contentsAsResource); String result = restTemplate.postForObject(urlForFacade, map, String.class); 

我重写了ByteArrayResource的getFilename,因为如果我不这样做,我会得到一个空指针exception(显然它取决于java激活.jar是否在类path上,如果是,它将使用文件名来尝试确定内容types)

前几天我也遇到了同样的问题。 谷歌search让我在这里和其他几个地方,但没有解决这个问题。 我最终将上传的文件(MultiPartFile)保存为tmp文件,然后使用FileSystemResource通过RestTemplate将其上传。 这是我使用的代码,

 String tempFileName = "/tmp/" + multiFile.getOriginalFileName(); FileOutputStream fo = new FileOutputStream(tempFileName); fo.write(asset.getBytes()); fo.close(); parts.add("file", new FileSystemResource(tempFileName)); String response = restTemplate.postForObject(uploadUrl, parts, String.class, authToken, path); //clean-up File f = new File(tempFileName); f.delete(); 

我仍然在寻找一个更优雅的解决scheme来解决这个问题。

 MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>(); parts.add("name 1", "value 1"); parts.add("name 2", "value 2+1"); parts.add("name 2", "value 2+2"); Resource logo = new ClassPathResource("/org/springframework/http/converter/logo.jpg"); parts.add("logo", logo); Source xml = new StreamSource(new StringReader("<root><child/></root>")); parts.add("xml", xml); template.postForLocation("http://example.com/multipart", parts); 

最近我为这个问题苦苦挣扎,花了我近3天才弄明白。 错误的请求错误可能不是由客户端如何发送请求引起的,而是由于服务器未configuration为处理多部分请求。 他是我必须做的才能让它工作:

pom.xml – 增加了commons-fileupload依赖(如果你没有使用依赖pipe理,例如maven,请下载并添加jar到你的项目中)

 <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-version}</version> </dependency> 

web.xml – 添加多部分filter和映射

 <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>/springrest/*</url-pattern> </filter-mapping> 

app-context.xml – 添加多部分parsing器

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

你的控制器

 @RequestMapping(value=Constants.REQUEST_MAPPING_ADD_IMAGE, method = RequestMethod.POST, produces = { "application/json"}) public @ResponseBody boolean saveStationImage( @RequestParam(value = Constants.MONGO_STATION_PROFILE_IMAGE_FILE) MultipartFile file, @RequestParam(value = Constants.MONGO_STATION_PROFILE_IMAGE_URI) String imageUri, @RequestParam(value = Constants.MONGO_STATION_PROFILE_IMAGE_TYPE) String imageType, @RequestParam(value = Constants.MONGO_FIELD_STATION_ID) String stationId) { // Do something with file // Return results } 

你的客户

 public static Boolean updateStationImage(StationImage stationImage) { if(stationImage == null) { Log.w(TAG + ":updateStationImage", "Station Image object is null, returning."); return null; } Log.d(TAG, "Uploading: " + stationImage.getImageUri()); try { RestTemplate restTemplate = new RestTemplate(); FormHttpMessageConverter formConverter = new FormHttpMessageConverter(); formConverter.setCharset(Charset.forName("UTF8")); restTemplate.getMessageConverters().add(formConverter); restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory()); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setAccept(Collections.singletonList(MediaType.parseMediaType("application/json"))); MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>(); parts.add(Constants.STATION_PROFILE_IMAGE_FILE, new FileSystemResource(stationImage.getImageFile())); parts.add(Constants.STATION_PROFILE_IMAGE_URI, stationImage.getImageUri()); parts.add(Constants.STATION_PROFILE_IMAGE_TYPE, stationImage.getImageType()); parts.add(Constants.FIELD_STATION_ID, stationImage.getStationId()); return restTemplate.postForObject(Constants.REST_CLIENT_URL_ADD_IMAGE, parts, Boolean.class); } catch (Exception e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); Log.e(TAG + ":addStationImage", sw.toString()); } return false; } 

这应该够了吧。 我尽可能多地添加了一些信息,因为我花了好几天的时间,把整个问题的各个部分拼凑在一起,我希望这会有所帮助。

我们的一个人做了类似的文件系统资源 。 尝试

 mvm.add("file", new FileSystemResource(pUploadDTO.getFile())); 

假设.getFile的输出是一个java File对象,它应该和我们的一样,它只有一个File参数。

您可以简单地使用MultipartHttpServletRequest

例:

  @RequestMapping(value={"/upload"}, method = RequestMethod.POST,produces = "text/html; charset=utf-8") @ResponseBody public String upload(MultipartHttpServletRequest request /*@RequestBody MultipartFile file*/){ String responseMessage = "OK"; MultipartFile file = request.getFile("file"); String param = request.getParameter("param"); try { System.out.println(file.getOriginalFilename()); System.out.println("some param = "+param); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)); // read file } catch(Exception ex){ ex.printStackTrace(); responseMessage = "fail"; } return responseMessage; } 

request.getParameter()中的参数名称必须与相应的前端名称相同。

注意,通过getFile()提取该文件,而通过getParameter()提取其他附加参数

如果你不得不发送一个多部分文件,其中包括一个需要使用特定的HttpMessageConverter进行转换的对象,无论您尝试什么,都会得到“不合适的HttpMessageConverter”错误,您可以尝试这个:

 RestTemplate restTemplate = new RestTemplate(); FormHttpMessageConverter converter = new FormHttpMessageConverter(); converter.addPartConverter(new TheRequiredHttpMessageConverter()); //for example, in my case it was "new MappingJackson2HttpMessageConverter()" restTemplate.getMessageConverters().add(converter); 

这为我解决了这个问题,自定义对象,与一个文件(instanceof FileSystemResource,在我的情况下),是我需要发送的多部分文件的一部分。 我尝试了TrueGuidance的解决scheme(还有很多在networking上发现的解决scheme)无济于事,然后我查看了FormHttpMessageConverter的源代码并尝试了这个。

我必须做同样的事情@Luxspes做以上..我使用的是Spring 4.2.6。 花了相当一段时间,为什么ByteArrayResource从客户端传输到服务器,但服务器不能识别它。

 ByteArrayResource contentsAsResource = new ByteArrayResource(byteArr){ @Override public String getFilename(){ return filename; } }; 

您必须将FormHttpMessageConverter添加到您的applicationContext.xml中才能发布多部分文件。

 <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.FormHttpMessageConverter" /> </list> </property> </bean> 

有关示例,请参见http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html