Sending Multipart files with RestTemplate - java

I have two servers, where server A is sending files to server B. On server B i have an endpoint which recieves files of given category. This is signature of endpoint i need to send files to:
#PostMapping("/uploadMultipleFiles/{projectId}")
public List<UploadFileResponseDts> uploadMultipleFiles(#RequestParam("files") MultipartFile[] files, #RequestParam("categoryId") Long categoryId, #PathVariable("projectId") Long projectId) {
return uploadMulitpleFiles(files, categoryId, projectId);
}
Now im having trouble creating such request with RestTemplate exchange. On Server A i'm trying to send files one by one (endpoint on server B needs to accept multipart array since its used somewhere else as well). This is what i tried:
public Optional<String> uploadFile(File file, Long projectId) throws Exception {
String authToken = getAccessToken();
String projectFileUploadEndpoint = fileUploadEndpoint + SEPARATOR + projectId;
FileInputStream input = new FileInputStream(file);
byte[] bytes = IOUtils.toByteArray(input);
MultiValueMap<String, Object> parts =
new LinkedMultiValueMap<>();
parts.add("files", new ByteArrayResource(bytes));
parts.add("categoryId", 0L);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("Authorization", authToken);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(parts, headers);
ResponseEntity<String> response =
restTemplate.exchange(projectFileUploadEndpoint ,
HttpMethod.POST, requestEntity, String.class);
return Optional.empty();
}
this almost works, the only issue is that on server B files from #RequestParam("files") MultipartFile[] files is always null. I assume that the issue is with the way im assembling parts in on my server A, however i cant find my mistake. Could you point it out?

in your controller all is fine, the problem is about your client code.
Basically you should use FileSystemResource object instead of ByteArrayResource.
The basic motivation is about file metadata. Using FileSystemResource spring is able to rebuild the required MultipartFile info, therefore the your code may be like below
#Test
public void contextLoads()
{
File file = new File("test.txt");
FileSystemResource fileSystemResource = new FileSystemResource(file);
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("categoryId", 0L);
parts.add("files", fileSystemResource);
String serviceUrl = "http://localhost:8080/uploadMultipleFiles/0";
RequestEntity<MultiValueMap<String, Object>> requestEntity = post(fromPath(serviceUrl).build().toUri())
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(parts);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new ResponseErrorHandler()
{
#Override
public boolean hasError(ClientHttpResponse response) throws IOException
{
return false;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException
{
System.out.println(response);
}
});
ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.POST, requestEntity, String.class);
assertThat(200, Is.is(response.getStatusCode()));
}
I hope that this test case can help you to solve the problem

Related

Spring boot - restTemplate.postForObject - params are null

I have 2 spring boot apps running one as a frontend and another as a backend service. From the frontend i make an api call to the backend service and both the parameters that i send show up as null. I think the problem is in the rest template.
UPDATE
So i have noticed if i omit the content value then it works. Since content is the content of a file that is larger than 1mb I added the following to application.yml:
spring.servlet.multipart.max-file-size: 10MB
spring.servlet.multipart.max-request-size: 10MB
Here is my code which I updated from one posted in this issue:
How to POST form data with Spring RestTemplate?
But i still don't get the value in the backend controller instead both values are null.
public void upload(byte[] content, String name) {
String encodedString = Base64.getEncoder().encodeToString(content);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("fileName", name);
map.add("content", encodedString);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And here is the controller in the backend. Both fileName and content are null:
#CrossOrigin
#SneakyThrows
#ResponseBody
#PostMapping(value = "/upload")
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] decodedBytes = Base64.getDecoder().decode(form.getContent());
uploadService.upload(decodedBytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Can anyone please see what is wrong with this code?
Thanks in advance.
I guess the problem is that you are trying to use restTemplate.postForObject but with #RequestParam and not a #RequestBody.
In #RequestParam you are expecting the data to be received in the query params /upload?fileName=&content=. But you are actually sending it in the body with the restTemplate.postForObject(backendService+ "/upload", map, String.class);.
So my suggestion is to change
public ResponseEntity<String> upload(#RequestParam(value = "fileName") String fileName, #RequestParam(value = "content") String content)
to
public ResponseEntity<String> upload(#RequestBody Map<String, String> body)
and then get fileName and fileContent from the body.
Ok i could fix it by sending and receiving bytes instead of bytes encoded as string.
So in the resttemplate:
public void upload(byte[] bytes, String name) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("fileName", name);
map.add("bytes", bytes);
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
log.debug("map values: " + map.toString());
ResponseEntity<String> response = restTemplate.postForEntity(backendUrl + "/upload", request, String.class);
log.debug("Response from upload: " + response);
}
And in the controller:
public ResponseEntity<String> upload(#ModelAttribute FormModel form) {
byte[] bytes = form.getBytes();
uploadService.upload(bytes, form.getFileName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Content-Type", "application/json");
return ResponseEntity.ok().headers(responseHeaders).body("Uploaded");
}
Still it would be good to know why the previous version didn't work.

Multipart files http request with Spring Rest Template arrives without the files

I have this client for sending multipart file http requests with Rest Template
#Component
public class RestTemplatePost {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#PostConstruct
public void prepareMessage() throws Exception {
File file = new File("****");
File file2 = new File("****");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
form.add("files", file);
form.add("files", file2);
form.add("usertoken", "test");
form.add("sendTo", "test);
form.add("subject", "test");
form.add("content", "test");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, httpHeaders);
String serverUrl = "http://localhost:8080/api/mails/send";
List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<>();
httpMessageConverters.add(new FormHttpMessageConverter());
httpMessageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate().setMessageConverters(httpMessageConverters);
restTemplate().postForEntity(serverUrl, requestEntity, String.class);
}
}
Then I have this server side that should receive the request:
#RestController
#RequestMapping("/api")
public class MainConroller {
private static final Logger log = LoggerFactory.getLogger(MainConroller.class);
#Autowired
private MainService mainService;
public MainConroller(MainService mainService) {
this.mainService = mainService;
}
#PostMapping("/mails/send")
public void send(
#RequestParam("usertoken") String usertoken,
#RequestParam("sendTo") String sendTo,
#RequestParam("subject") String subject,
#RequestParam("content") String content,
#RequestParam(required = false, name = "files") List<MultipartFile> multipartFiles) {
log.debug("{}, {}, {}, {}", usertoken, sendTo, subject, content);
mainService.processMessage(usertoken, sendTo, subject, content, multipartFiles);
}
}
When I send this request from the client side, everything arrives on the server side except the files.
The RequestParam files is empty after receiving the request.
Update
Message converters explicitly removed as recommended, nothing changed.
No files are sent because there is no HttpMessageConverter capable of converting a java.io.File.
On the other hand, there is a ResourceHttpMessageConverter which converts all kinds of Resource, including FileSystemResource.
PS FormHttpMessageConverter is added by default when RestTemplate is created and FormHttpMessageConverter also uses it under the hood.

Upload file to Jersey web services with spring MultipartFile

I have a 3 step process to upload a file on a server:
Using EXT.JS form, I upload a file to a controler.
In the controler, the method gets the MultipartFile, builds a rest call using rest template and send it to the server.
On the server, the specified method should receive the multipart data and process the file.
Here is the method at the step 2 that get the file from the UI side (EXT.JS):
#RequestMapping("/customerUploadFile/upload")
#ResponseBody
public JsonResponse uploadFile(CustomerUploadBean bean,
#RequestParam("filePath") MultipartFile filePath) throws IOException {
long fileSize = filePath.getSize();
HttpEntity<CustomerUploadBean> httpEntity = getHttpEntity(bean);
byte[] byteArr = filePath.getBytes();
MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
parameters.add("userId", httpEntity.getHeaders().get("userId"));
parameters.add("file", byteArr);
// to set content type of header
org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<JsonResponse> jsonResponse = restTemplate.exchange(uri, HttpMethod.POST,
new HttpEntity<MultiValueMap<String, Object>>(parameters, headers),
new ParameterizedTypeReference<JsonResponse>() {
});
JsonResponse response = jsonResponse.getBody();
if (jsonResponse.getStatusCode() == HttpStatus.OK) {
response.setSuccess(true);
} else {
response.setSuccess(false);
}
return response;
}
I've verified, the filePath object have the file and contains information about the file.
At the step 3, here is the method in the controller on the server that awaits for the file to process it:
public Response importUserProfileCSV(
#ApiParam(value = "Service Name", required = true) #PathParam("service") String service,
#ApiParam(value = "CSV file to upload.", required = true) #FormDataParam("file") InputStream uploadedInputStream,
#ApiParam(value = "CSV file detail", required = true) #FormDataParam("file") FormDataContentDisposition fileDetail) {
return delegate.importUserProfileCSV(uploadedInputStream, fileDetail,
service, "user");
}
The problem here is that at the step 3, in the method right above, the fileDetail object contains only null values... How can I adapt the method of step 2 (spring framework) to the method of step 3 (jersey).

How to send JSON as a Input parameter from one Microservice to another using RestTemplate in Spring Boot

I want to send JSON as an input from Microservice M1 to a Microservice M2.
M1 and M2 both are on different machines.
I am new to Spring Boot,
I found some code but I am unable to get it.
Please help.
make a class on both microservices or make a jar of that class and add to both microservices so that they both can access the same data.
Lets say the class is
class TestData{
private String name;
private String id;
// getters and setters
}
Now you can send data from M1 to M2 as following
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
TestData data = new TestData();
HttpEntity<?> entity = new HttpEntity<Object>(data,headers);
ResponseEntity<Object> responseEntity = restTemplate.exchange("url", HttpMethod.POST, entity, Object.class);
In Microservice M2 you can write a controller to get the data and process it as follows
#RequestMapping(value="/url",method=RequestMethod.POST)
public Object do(#RequestBody TestData data){
// do something
return //something
}
Let's Say Your Have MicroService1 which needs to send JSONObject => JsonObject to another MicroService2 which is on different Machine but on same network .
Sender Side:
RestTemplate restTemplate = new RestTemplate();
String jsonString = restTemplate.postForObject("http://10.177.7.128:8080/user/insertJsonObject",jsonObject,String.class);
Syntax for restTemplate.postForObject is:
ResponseType var1 = restTemplate.postForObject("network ip Address:portnumber/path",JSONObject,ResponseType)
To Know the URI go to System Preferences > Network
To Receive the object at the receiver Side
#RequestMapping(value="/user/insertJsonObject", method=RequestMethod.POST)
public String updateProductSold(#RequestBody JSONObject jsonObject) {
...Body
...
...
return responseStatus;
Here is the sample code
public class Test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.1168.1.190:8080/test" // url for second service
System.out.println(responserEntityValue(jsonString,restTemplate,url,HttpMethod.POST,String.class));
}
public ResponseEntity<String> responserEntityValue(final String body, final RestTemplate restTemplate,
final String uRL, final HttpMethod requestMethod, final Class<String> stringClass) {
HttpHeaders headers = new HttpHeaders();
// Set all headers
headers.add(DatabaseConstants.CONTENT_TYPE, "application/json");
HttpEntity<String> request = new HttpEntity<>(body, headers);
return restTemplate.exchange(uRL, requestMethod, request, stringClass);
}

How to pass raw XML into RESTTemplate POST or PUT method without converting it?

I am trying to update or create xml file if not present. Then I use code below to send the file using PUT method of a service.
public void importClusterProperties(RestManPropertyHolder propertyHolder,File file,String id) throws RestManServiceException {
testRestTemplate = new TestRestTemplate(propertyHolder.getSbusUserName(), propertyHolder.getSbusUserPassword());
String sbusUrl = utils.prepareGatewayURI(propertyHolder);
try {
HttpHeaders requestHeaders = new HttpHeaders();
List <MediaType> mediaTypeList = new ArrayList<MediaType>();
mediaTypeList.add(MediaType.APPLICATION_ATOM_XML);
requestHeaders.setAccept(mediaTypeList);
requestHeaders.setContentType(MediaType.APPLICATION_ATOM_XML);
HttpEntity<String> requestEntity = new HttpEntity<String>(requestHeaders);
// Create the HTTP PUT request,
ResponseEntity<String> response = testRestTemplate.exchange(sbusUrl + "/clusterproperty?",HttpMethod.PUT, requestEntity,String.class);
if (null != response) {
System.out.println("RESPONSE::" + response.toString());
}
} catch (RestClientException rce) {
System.out.println("REST EXCEPTION:::" + rce.getMessage());
}
}
How to pass raw xml file into RestTemplate without converting it first into a java object?
enter image description here
Convert file to byte array and send it using ByteArrayHttpMessageConverter.
RestTemplate restTemplate = new RestTemplate();
// Add ByteArrayHttpMessageConverter if not present by default.
restTemplate.getMessageConverters().add(
new ByteArrayHttpMessageConverter());
String fileName = "path + file name";
// FileUtils is from Apache Commons IO
// import org.apache.commons.io.FileUtils;
byte[] requestBody = FileUtils.readFileToByteArray(new File(fileName));
HttpEntity<byte[]> requestEntity = new HttpEntity<byte[]>(requestBody , requestHeaders);
// Create the HTTP PUT request,
ResponseEntity<byte[]> response =
restTemplate.exchange("URL ...." , HttpMethod.PUT ,
requestEntity , byte[].class);

Categories

Resources