2010-11-07 35 views
34

Tôi đang làm việc với Spring 3 và RestTemplate. Về cơ bản, tôi có hai ứng dụng và một trong số đó phải đăng các giá trị cho ứng dụng khác. thông qua mẫu còn lại.Gửi tệp Multipart dưới dạng tham số POST với các yêu cầu RestTemplate

Khi giá trị để đăng là Strings, nó hoạt động hoàn hảo, nhưng khi tôi phải đăng các tham số hỗn hợp và phức tạp (như MultipartFiles), tôi nhận được một ngoại lệ chuyển đổi.

Một ví dụ, tôi có điều này:

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

Ở phía bên kia ... tôi có một ứng dụng web (App2) tiếp nhận các thông số từ 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"; 
} 

My ứng dụng 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> 

Đây là chồng ngoại lệ mà tôi nhận được khi tôi làm postForObject của RestTemplate. ..

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) 

Vì vậy, câu hỏi của tôi là:

  1. Có thể gửi MultipartFile thông qua RestTemplate bằng POST không?
  2. Có một số trình chuyển đổi cụ thể mà tôi phải sử dụng để gửi loại đối tượng này không? Ý tôi là có một số MultipartFileHttpMessageConverter để sử dụng trong cấu hình của tôi?
+4

Tsk tsk, chửi thề trong mã? Hy vọng nó không đi vào sản xuất. – Spedge

Trả lời

9
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); 
+1

Không phải là logic của câu trả lời này chính xác giống như những gì op đang làm? Tôi nhận được cùng một lỗi mà anh ta nhận được nếu tôi nhận phản hồi này và chỉ dán vào mã của tôi thay cho yêu cầu của riêng tôi. Tôi nghĩ câu hỏi thực sự ở đây là tại sao 'không có HttpMessageConverter phù hợp được tìm thấy cho yêu cầu' khi sử dụng LinkedMultiValueMap cho một số người dùng, chứ không phải cho người khác? Có thư viện nào cần liên kết không? – deepwinter

14

Tôi cũng gặp vấn đề tương tự vào ngày khác. Google tìm kiếm đã cho tôi ở đây và một số nơi khác, nhưng không ai đưa ra giải pháp cho vấn đề này. Cuối cùng tôi đã lưu tệp đã tải lên (MultiPartFile) dưới dạng tệp tmp, sau đó sử dụng FileSystemResource để tải tệp lên qua RestTemplate. Đây là mã tôi sử dụng,

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

Tôi vẫn đang tìm giải pháp thanh lịch hơn cho vấn đề này.

+0

Tôi không rõ lý do tại sao bạn nhận tệp được tải lên rồi tải lên lại, nhưng giả sử bạn có lý do hợp lệ để thực hiện điều đó, bạn có thể sử dụng phương thức MultipartFile.transferTo() (http://bit.ly/Pm6sPN) thay vì cuộc gọi FileOutputStream.write(), mà là một chút sạch hơn so với cách bạn đang làm nó. Nếu không, thủ thuật thực sự ở đây là việc sử dụng FileSystemResource. Nó được xử lý bởi ResourceHttpMessageConverter. Nếu không (và tôi ngạc nhiên điều này không tồn tại), bạn cần phải viết một công cụ chuyển đổi đặc biệt để xử lý tập tin đa. –

+0

FileSystemResource chỉ hoạt động, nếu chúng ta biết đường dẫn chính xác của tệp. Điều này không đạt được, do các vấn đề về bảo mật của trình duyệt. – user754657

3

Một trong những kẻ của chúng tôi thực hiện điều gì đó tương tự với hệ thống tệp nguồn. thử

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

giả định đầu ra của bạn .getFile được một đối tượng java File, nên hoạt động giống như chúng ta, mà chỉ có một tham số tập tin.

40

Một cách để giải quyết điều này mà không cần sử dụng FileSystemResource yêu cầu tệp trên đĩa, là sử dụng ByteArrayResource, theo cách đó bạn có thể gửi mảng byte trong bài đăng của mình (mã này hoạt động với Spring 3.2.3):

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

tôi ghi đè getFilename của ByteArrayResource vì nếu tôi làm tôi không nhận được một con trỏ ngoại lệ null (dường như nó phụ thuộc vào việc kích hoạt java .jar là trên classpath, nếu nó là , nó sẽ sử dụng tên tệp để cố gắng xác định loại nội dung)

+3

Kiểu dữ liệu của "nội dung" là gì? – user754657

+0

@Luxspes làm thế nào bạn có thể ghi đè lên phương pháp cuối cùng của lớp ByteArrayResource? Điều này là không thể. – xyz

+1

@xyz phương thức getFilename không phải là cuối cùng: http://docs.spring.io/autorepo/docs/spring/3.2.3.RELEASE/javadoc-api/org/springframework/core/io/AbstractResource.html#getFilename () – Luxspes

7

Gần đây tôi đã gặp khó khăn với vấn đề này, đã mất gần 3 ngày để tìm ra. Lỗi yêu cầu không đúng có thể không được gây ra bởi cách máy khách gửi yêu cầu nhưng vì máy chủ không được cấu hình để xử lý các yêu cầu nhiều phần. Mình là những gì tôi phải làm gì để có được nó làm việc:

pom.xml - Thêm commons-FileUpload phụ thuộc (tải về và thêm jar để dự án của bạn nếu bạn không sử dụng quản lý phụ thuộc như maven)

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

web.xml - Thêm bộ lọc nhiều phần dữ liệu và lập bản đồ

<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 - Thêm nhiều phần dữ liệu phân giải

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

điều khiển của bạn

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

Khách hàng của bạn

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

Điều đó sẽ làm các trick. Tôi đã thêm càng nhiều thông tin càng tốt vì tôi đã dành nhiều ngày, kết hợp các bit và phần của vấn đề đầy đủ, tôi hy vọng điều này sẽ hữu ích.

1

Nếu bạn phải gửi tệp nhiều phần được tạo, trong số những thứ khác, bởi một đối tượng cần được chuyển đổi bằng một HttpMessageConverter cụ thể và bạn nhận được lỗi "không thích hợp HttpMessageConverter" bất kể bạn thử gì, bạn có thể muốn thử với điều này:

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

này đã giải quyết được vấn đề đối với tôi với một đối tượng tùy chỉnh mà, cùng với một tập tin (instanceof FileSystemResource, trong trường hợp của tôi), là một phần của tập tin nhiều phần dữ liệu tôi cần để gửi. Tôi đã thử với giải pháp TrueGuidance (và nhiều giải pháp khác được tìm thấy trên web) để không có kết quả, sau đó tôi đã xem mã nguồn của FormHttpMessageConverter và đã thử cách này.

0

Bạn phải thêm FormHttpMessageConverter vào tệp applicationContext.xml của mình để có thể đăng nhiều tệp.

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

Xem http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/FormHttpMessageConverter.html để biết ví dụ.

3

Bạn chỉ có thể sử dụng MultipartHttpServletRequest

Ví dụ:

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

tên trong request.getParameter() đâu thông số phải giống nhau tương ứng với tên frontend.

Note, tập tin đó được chiết xuất qua getFile() trong khi thông số bổ sung khác được chiết xuất qua getParameter()

2

tôi phải làm điều tương tự mà @Luxspes đã above..and Tôi đang sử dụng Spring 4.2.6. Đã dành khá nhiều thời gian để tìm ra lý do tại sao ByteArrayResource được chuyển từ máy khách sang máy chủ, nhưng máy chủ không nhận ra nó.

ByteArrayResource contentsAsResource = new ByteArrayResource(byteArr){ 
      @Override 
      public String getFilename(){ 
       return filename; 
      } 
     }; 
Các vấn đề liên quan