4

assume we have a one controller on third party service which accepts multipart files and its code is like (assume it's running on localhost:9090)

@RequestMapping("/file")
@RestController
public class FileController {

    @RequestMapping(value = "/load", method = RequestMethod.POST)
    public String getFile(@RequestPart("file") MultipartFile file){
        return file.getName();
    }

}

The question is: How write a correct code in my controller, with RestTemplate, that calls the third party service, with file in body?

A few examples that do not work:

First one:

@RequestMapping("/file")
@RestController
public class FileSendController {

    private RestTemplate restTemplate = new RestTemplate();

    @RequestMapping(value = "/send", method = RequestMethod.POST)
    public ResponseEntity<?> sendFile(@RequestPart MultipartFile file) 
    throws IOException {
    String url = "http://localhost:9090/file/load";
    return restTemplate.postForEntity(url, file.getBytes(), 
    ResponseEntity.class);
    }
}

Second one:

@RequestMapping("/file")
@RestController
public class FileSendController {

    private RestTemplate restTemplate = new RestTemplate();

    @RequestMapping(value = "/send", method = RequestMethod.POST)
    public ResponseEntity<?> sendFile(@RequestPart MultipartFile file) 
    throws IOException {
        String url = "http://localhost:9090/file/load";
        byte[] bytes = file.getBytes();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<byte[]> entity = new HttpEntity<>(bytes, headers);
        return restTemplate.exchange(url, HttpMethod.POST, 
        entity,ResponseEntity.class);
    }
}

One restriction: i should load files from memory, so it forces me to use byte[]

All of this examples throw 500 on third party service with message: org.springframework.web.multipart.MultipartException: Current request is not a multipart request.

Thanks for your advices.

Dmitry
  • 63
  • 1
  • 3

1 Answers1

13

Try this:

MultiValueMap<String, Object> data = new LinkedMultiValueMap<String, Object>();
ByteArrayResource resource = new ByteArrayResource(file.getBytes()) {
        @Override
        public String getFilename() {
            return file.getName();
        }
};
data.add("file", resource);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(data, requestHeaders);

final ResponseEntity<Response<ImportDto>> responseEntity = restTemplate.exchange(url, 
                    HttpMethod.POST, requestEntity, new ParameterizedTypeReference<Response<ResponseDto>>(){});
Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
  • It throws 400 with message: Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Unrecognized token 'file': was expecting 'null', 'true', 'false' or NaN – Dmitry Apr 24 '17 at 20:22
  • Can you try changing the controller to use `@RequestBody` instead? – Darshan Mehta Apr 24 '17 at 20:28
  • Also, `RequestMapping` does not seem to have `consumes` value. It should be `multipart/form-data`. – Darshan Mehta Apr 24 '17 at 20:45
  • If i call third party controller with postman, it works. But if i do call to my controller that calls third party, is always throw exception. Addinition consumes = MediaType.MULTIPART_FORM_DATA_VALUE didn't change anything. :( – Dmitry Apr 25 '17 at 04:49
  • @Dmitry does it print line number in the Exception stacktrace? If it works with postman, you can try comparing the request payload for both postman and RestTemplate requests and see how/where they differ. – Darshan Mehta Apr 25 '17 at 07:18
  • i'll try, but confusing thing is if I override postman content-type as multipart/form-data, postman will throw same exception as my controller. If I let postman add header by himself, (he choses the same content type as i do, and i didnt see any diff) there is no exception. The exception is 500 "Could not parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found" – Dmitry Apr 25 '17 at 07:53
  • Yeah, in case of postman we don't need to set that header explicitly. – Darshan Mehta Apr 25 '17 at 07:54
  • 1
    Your solution works. I missed the anonymous part. I did not think it was necessary. Thanks for your help. – Dmitry Apr 25 '17 at 12:27
  • Thanks @Dmitry the getFilename Override did it for me too!! – GiovanyMoreno Aug 07 '20 at 17:10
  • @DarshanMehta Worked for me also Thanks a lot , But I have question, Like 1) why this anonymous part is required ? 2) is there any documentation link where i can read about file upload in Spring 5 and Spring boot 2.X.X – Vivek Kamble Aug 11 '20 at 14:01