25

The file i'm trying to upload will always be a xml file. I want to set the content-type as application/xml Here is my code:

         MultiValueMap<String, Object parts = new LinkedMultiValueMap<String,
         Object(); parts.add("subject", "some info"); 
         ByteArrayResource xmlFile = new    ByteArrayResource(stringWithXMLcontent.getBytes("UTF-8")){
                 @Override
                 public String getFilename(){
                     return documentName;
                 }             
             };

     parts.add("attachment", xmlFile);

//sending the request using RestTemplate template;, the request is successfull 
String result = template.postForObject(getRestURI(), httpEntity,String.class);      
//but the content-type of file is 'application/octet-stream'

the raw request looks like this:

    Content-Type:
    multipart/form-data;boundary=gbTw7ZJbcdbHIeCRqdX81DVTFfA-oteHHEqgmlz
    User-Agent: Java/1.7.0_67 Host: some.host Connection: keep-alive
    Content-Length: 202866

    --gbTw7ZJbcdbHIeCRqdX81DVTFfA-oteHHEqgmlz Content-Disposition: form-data;    name="subject" Content-Type: text/plain;charset=ISO-8859-1
    Content-Length: 19

    some info

    --gbTw7ZJbcdbHIeCRqdX81DVTFfA-oteHHEqgmlz Content-Disposition: form-data;   name="attachment"; filename="filename.xml" Content-Type:
    application/octet-stream Content-Length: 201402

    ....xml file contents here ..

The content-type of the file is being generated as 'application/octet-stream' where as i want it to be 'application/xml' How can i set the content type for the file?

RGR
  • 681
  • 1
  • 6
  • 9
  • Take a look at this: http://stackoverflow.com/questions/11579621/spring-resttemplate-postforobject-with-header-webservice-cant-find-my-header-p or http://stackoverflow.com/questions/3616359/who-sets-response-content-type-in-spring-mvc-responsebody – Dan Feb 06 '15 at 19:49

4 Answers4

43

I figured out the solution after taking hint from this link:

Making a multipart post request with compressed jpeg byte array with spring for android

Solution is to put the ByteArrayResource in a HttpEntity with required header and add the HttpEntity to Multivaluemap (Instead of adding ByteArrayResource itself.)

Code:

Resource xmlFile = new ByteArrayResource(stringWithXMLcontent.getBytes("UTF-8")){
            @Override
            public String getFilename(){
                return documentName;
            }
        };
        HttpHeaders xmlHeaders = new HttpHeaders();
        xmlHeaders.setContentType(MediaType.APPLICATION_XML);
        HttpEntity<Resource> xmlEntity = new HttpEntity<Resource>(xmlFile, xmlHeaders);
        parts.add("attachment", xmlEntity);
Community
  • 1
  • 1
RGR
  • 681
  • 1
  • 6
  • 9
  • saved my day too ! – Faraway Mar 09 '18 at 00:27
  • another day saved (at least half :) – itstata Jun 01 '18 at 11:11
  • https://medium.com/@voziv/posting-a-byte-array-instead-of-a-file-using-spring-s-resttemplate-56268b45140b This is also a good blog – Coder Jun 21 '19 at 12:18
  • Fantastic solution! If I may point out that the getFilename() method is essential. I initially tried without overriding it, and my target service would not recognize the multipart as a MultipartFile at all, it drove me nuts (I was hitting this problem: https://stackoverflow.com/questions/27101931/required-multipartfile-parameter-file-is-not-present-in-spring-mvc.). By simply adding the getFileName() method, the target service magically worked. – NotSoOldNick Jul 10 '19 at 05:19
2

As i can not comment the answer of @RGR I'm posting this as new answer although RGR's answer is absolutely correct.

The problem is, that the Sprint RestTemplates uses FormHttpMessageConverter to send the multi part request. This converter detects everything that inherits from Resource and uses this as the request's "file" part. e.g. If you use a MultiValueMap every property you add will be send in it's own part as soon as you add a "Resource"...--> Setting filename, Mime-Type, length,.. will not be part of the "file part".

Not an answer, but it's the explanation why ByteArrayResource must be extended to return the filename and be send as the only part of the request. Sending multiple files will work with a MultiValueMap

It looks like this behaviour was fixed in Spring 4.3 by SPR-13571

Nesta
  • 21
  • 4
2

This is how I handle file upload and receiving an uploaded file:

public String uploadFile(byte[] fileContent, String contentType, String filename) {
    String url = "https://localhost:8000/upload";
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
    ContentDisposition contentDisposition = ContentDisposition.builder("form-data")
        .name("file")
        .filename(filename)
        .build();
    fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
    fileMap.add(HttpHeaders.CONTENT_TYPE, contentType);
    HttpEntity<byte[]> entity = new HttpEntity<>(fileContent, fileMap);
    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    body.add("file", entity);
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
    ResponseEntity<String> response = template.exchange(url, HttpMethod.POST, requestEntity, String.class);
    return response.getBody();
}

And I consume it like this with the service listening at port 8000:

@Controller
@RequestMapping("upload")
public class FileUploadController {

    @PostMapping("")
    public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) {
        handleUploadedFile(
            file.getSize(), 
            file.getBytes(), 
            file.getContentType(), 
            file.getOriginalFilename()
        );
    }

}
Kent Munthe Caspersen
  • 5,918
  • 1
  • 35
  • 34
0

I've not used RestTemplate but i've used HttpClient in past - This is how i pass the body part -

MultipartEntityBuilder eb = MultipartEntityBuilder.create().setBoundary(MULTIPART_BOUNDARY)
                .addTextBody(BODYPART_ENTITY, key, ContentType.create("application/xml", Charset.forName("UTF-8")));

You will have to look at for API in RestTemplate which can take content-type

Pankaj
  • 3,512
  • 16
  • 49
  • 83