2

I have a PUT API to call to send my data that expects a Multipart Request. (Swagger has the API listed as Paramter Type formData; Data Type file).

I have this code working fine via Apache's Http Library, but to match the rest of the program I would like to use Spring Rest Template to make the same call.

//Via Apache:
Uri uri = "http://putmyresults.com";
ResultObject results = buildResultObject(myData);
MultipartEntityBuilder meb = MultipartEntityBuilder.create();
meb.addBinaryBody("file", results.convertToBytes(), ContentType.create("application/octet-stream"),"MyResultFile");
HttpEntity entity = meb.build();
put.setEntity(entity);

put.setHeader(new BasicHeader("Authorization","myToken"));
put.setHeader("Accept","application/json,application,octet-stream");
getMyCloseableHttpClient().execute(put);
//process response codes...



//Sample working REST for GET request:
Uri uri = "http://getmyresults.com/section5/resultId?sectionId=foo";
RestTemplate rest = new RestTemplate;

org.springframework.http.HttpEntity header = new HttpHeaders();
header.set("Authorization","myToken");
header.setContentType(MediaType.APPLICATION_JSON);
header.setAccept(Arrays.asList(MediaType.APPLICATION_JSON,MediaType.APPLICATION_OCTET_STREAM));

HttpEntity<String> entity = new HttpEntity("parameters", headers);
rest.exchange(uri, HttpMethod.GET, entity, Resource.class);
//process response...

But I can't seem to make the PUT request via REST. This is what I have so far:

Uri uri = "http://putmyresults.com";
MultiValueMap<String,Object> map = new LinkedMultiValueMap<>();
ResultObject results = buildResultObject(myData);
byte[] bytes = results.convertToBytes();

HttpHeaders header = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
org.springframework.http.HttpEntity<byte[]> entity = new HttpEntity<>(bytes,headers);
map.add("MyResultFile",entity);

HttpHeaders tokenHeader = new HttpHeaders();
tokenHeader.set(new BasicHeader("Authorization","myToken"));
tokenHeader.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> tokenEntity = new HttpEntity("parameters", headers);
map.add("parameters",tokenEntity);

org.springframework.http.HttpEntity<<MultiValueMap<String,Object> requestMap = new org.springframework.http.HttpEntity<>(map);
new RestTemplate().exchange(uri,HttpMethod.PUT,requestMap,String.class);

Server Header:

@PutMapping(path = "/xyz")
public ResponseEntity putRequest{
    HttpServletRequest request,
    HttpServletResponse response,
    @PathVariable String id,
    @RequestParam("file") MultipartFile uploadedFile,
    @RequestParam String sample,
    @RequestParam String sample2 throws Exception  {
        uploadedFile.getBytes(); ...
    }
}

Thanks for your help.

CeePlusPlus
  • 803
  • 1
  • 7
  • 26

3 Answers3

3

Try this:

MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();

// creating an HttpEntity for the binary part
HttpHeaders header = new HttpHeaders();
pictureHeader.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<ByteArrayResource> file = new HttpEntity<>(pngPicture, pictureHeader);
multipartRequest.add("file", file);

HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<> 
(multipartRequest, header);
ResultObject result = restTemplate.put(UPLOAD_URL, requestEntity);
brijesh
  • 760
  • 6
  • 4
  • `Cant infer arguments for ByteArrayResource` -- can you please expand the example and match the variables to what I have? I'm not clear what is my byte[] in this example. (I've force added ByteArrayResource into the Diamonds, but then the error is `ByteArrayResource cannot be applied to byte[]` – CeePlusPlus Sep 24 '19 at 15:48
2

You need to add the content type as multipart along with content disposition. Make sure to import spring classes as there are same classes in Apache Http library.

String uri = "http://putmyresults.com";
MultiValueMap<String,Object> multipartRequest = new LinkedMultiValueMap<>();

ResultObject results = buildResultObject(myData);
byte[] bytes = results.convertToAvroBytes()
HttpHeaders fileHeader = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("file","mydata");
HttpEntity<byte[]> entity = new HttpEntity<>(bytes,fileHeader);
multipartRequest.add("file",entity);

HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.MULTIPART_FORM_DATA);

HttpEntity<MultiValueMap<String,Object>> requestMap = new HttpEntity<>(multipartRequest,header);
String response = new RestTemplate().exchange(uri,HttpMethod.PUT,requestMap,String.class);

Reference:multipart-file-upload-using-spring-rest-template-spring-web-mvc

s7vr
  • 73,656
  • 11
  • 106
  • 127
  • You were first, but its still not working. Current error is `org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present` -- I've add the servers source code to the question. – CeePlusPlus Sep 24 '19 at 15:47
  • 1
    Sorry, Could you try `multipartRequest.add("file",entity);` instead of `multipartRequest.add("MyResultFile",entity);` ? – s7vr Sep 24 '19 at 15:52
  • what error do you receive ? Is it complaining about the file name ? – s7vr Sep 24 '19 at 16:11
  • That doesn't look like an error coming from rest template but from controller. Are you sure your multipart request is making to the controller correctly ? – s7vr Sep 24 '19 at 16:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/199923/discussion-between-ceeplusplus-and-user2683814). – CeePlusPlus Sep 24 '19 at 16:16
  • Added the missing content disposition header. It should all work now. – s7vr Sep 24 '19 at 22:56
  • something even before the server is blocking it now, it doesnt seem to even reach Spring if I add that header. I verified that line is causing this issue now. – CeePlusPlus Sep 25 '19 at 18:56
  • I tried the same code at my end and worked as expected. are you just trying with first multipart only ? I removed the 'parameters' in my test. See if you can locate any error somewhere in logs. – s7vr Sep 25 '19 at 19:00
  • With no params it also doesnt hit the logs, regardless of that header their or not. With the parameters and not that header I'm back to 'file' is not present. My code currently matches my post. – CeePlusPlus Sep 25 '19 at 19:13
  • To be honest. I didnt try with all other request parameters you have. I tried with `@PutMapping(path = "/xyz") public ResponseEntity putRequest( HttpServletRequest request, HttpServletResponse response, @RequestParam("file") MultipartFile uploadedFile ) throws Exception { uploadedFile.getBytes(); ...` } } and worked for me. – s7vr Sep 25 '19 at 19:15
  • Everything else is just path variables that make up the uri – CeePlusPlus Sep 25 '19 at 19:16
  • You could add `header.set("Authorization","myToken");` if its complaining about the authorization. If that doesn't work please see if you can locate the error. May be trying running your server at debug log level. This should now match exactly as what your Apache version. – s7vr Sep 25 '19 at 20:32
  • Added my answer that builds on yours. Seems to be working for me. Thank you so much for your help. – CeePlusPlus Sep 26 '19 at 17:47
0

With the help of @user2683814 and this article, this is what works for me:

import org.springframework.http.*; //Used with 5.1.9.RELEASE

Uri uri = "http://putmyresults.com";
byte[] bytes = MyObject.getMyBytes();

MultiValueMap<String,String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition cd = ContentDisposition.builder("form-data").
name("file").filename("myFile").build();
filemap.add(HttpHeaders.CONTENT_DISPOSITION,cd.toString());

HttpEntity<byte[]> fileEntity = new HttpEntity<>(bytes,fileMap);
MultiValueMap<String,Object> body = new LinkedMultiValueMap<>();
body.add("file",fileEntity);


HttpHeaders tokenHeader = new HttpHeaders();
tokenHeader.set("Authorization","myToken");
tokenHeader.setContentType(MediaType.MULITPART_FORM_DATA);

HttpEntity<MultiValueMap<String,Object>> requestMap = new HttpEntity<>(body,securedTokenHeaders);


new RestTemplate().exchange(uri,HttpMethod.PUT,requestMap,String.class)

CeePlusPlus
  • 803
  • 1
  • 7
  • 26