Sending Multipart File as POST parameters with RestTemplate requests

I am working with Spring 3 and RestTemplate. I have basically, two applications and one of them have to post values to the other app. through rest template.

When the values to post are Strings, it's work perfect, but when i have to post mixed and complex params (like MultipartFiles) i get an converter exception.

As example, i have this:

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

On the other side... i have another web application (App2) that receives the parameters from the 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 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>  

Here is the stack of the exception that i am getting when i do the postForObject of the 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)

So my questions are:

  1. Is it possible to send MultipartFile through RestTemplate using POST?
  2. Are there some specific converters that I have to use to send this type of objects? I mean is there some MultipartFileHttpMessageConverter to use in my configuration?

A way to solve this without needing to use a FileSystemResource that requires a file on disk, is to use a ByteArrayResource, that way you can send a byte array in your post (this code works with 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);

I override the getFilename of the ByteArrayResource because if I don't I get a null pointer exception (apparently it depends on whether the java activation .jar is on the classpath, if it is, it will use the file name to try to determine the content type)


I also ran into the same issue the other day. Google search got me here and several other places, but none gave the solution to this issue. I ended up saving the uploaded file (MultiPartFile) as a tmp file, then use FileSystemResource to upload it via RestTemplate. Here's the code I use,

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

I am still looking for a more elegant solution to this problem.


I recently struggled with this issue for 3 days. How the client is sending the request might not be the cause, the server might not be configured to handle multipart requests. This is what I had to do to get it working:

pom.xml - Added commons-fileupload dependency (download and add the jar to your project if you are not using dependency management such as maven)

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

web.xml - Add multipart filter and mapping

<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 - Add multipart resolver

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

Your Controller

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

Your client

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

That should do the trick. I added as much information as possible because I spent days, piecing together bits and pieces of the full issue, I hope this will help.


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

One of our guys does something similar with the filesystemresource. try

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

assuming the output of your .getFile is a java File object, that should work the same as ours, which just has a File parameter.