I am trying to upload a zip file and form data using RestTemplate.
Please find the code below.
#Controller
#RequestMapping("/test")
public class EdicomGatewayClient {
#RequestMapping(value ="/publisEdicomDocument", method = RequestMethod.POST)
public void publisEdicomDocument() {
List<ApiError> errors = null;
try {
RestTemplate restTemplate = new RestTemplate();
String restURL = "";
File file = new File("");
DiskFileItem fileItem = new DiskFileItem("file", "text/plain", false, file.getName(), (int) file.length(), file.getParentFile());
fileItem.getOutputStream();
MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<String, Object>();
multipartRequest.add("user","OPENTEXT_AR_TST");
multipartRequest.add("password","4t4qqdbddp");
multipartRequest.add("domain","OPENTEXT_AR_TST");
multipartRequest.add("group","ASPEDI41");
multipartRequest.add("publishType","2");
multipartRequest.add("process","MAPA_AFIP_WSMTXCA_EDICOM_OPENTEXT");
multipartRequest.add("saveOriginalImage",false);
multipartRequest.add("sendDocument",true);
multipartRequest.add("returnData",false);
multipartRequest.add("returnDataType","2");
multipartRequest.add("message", new ByteArrayResource(multipartFile.getBytes()));
multipartRequest.add("filename", multipartFile.getOriginalFilename());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(multipartRequest, headers);
ResponseEntity response = restTemplate.exchange(restURL,HttpMethod.POST, requestEntity, String.class);
System.out.println("Response ******************************************:"+response);
} catch (Exception e) {
}
}
public static void main(String[] args) {
EdicomGatewayClient edicom = new EdicomGatewayClient();
edicom.publisEdicomDocument();
}
}
This is giving me the following error:
org.springframework.web.client.HttpClientErrorException: 400 bad Request.
Related
I am new to resttemplate and trying to understand how to invoke below API using restTemplate
public ResponseEntity<String> upload(#ResquestPart("file") MultipartFIle file,
#RequestParam("path") String path){
//businness logic
}
I am trying to call above as below however it fails on "java.io.filenotfoundexception multipartfile resource [ABC.txt]cannot be resolved to absolute file path"
public void uploadFile() {
Path path = Paths.get("C:/ABC.txt");
byte[] content = null;
try{
content = Files.readAllBytes(path); // All file is read in content variable
} catch(final IOException e){
}
MultipartFile file = new MockMultipartFile("ABC.txt",content);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setContentType("Accept", Mediatype.APPLICATION_JSON_VALUE);
headers.setContentType("Content-type", Mediatype.APPLICATION_JSON_VALUE);
MultiValueMap<String, Object> obj = new LinkedMultiValueMap<String, Object>();
obj.add("file", file);
obj.add9("path", "/opt/apps");
HttpEntity<?> requestEntity = new HttpEntity<>(obj, headers);
String result = getRestTemplate().postForEntity("url", requestEntity, String.class);
}
Right now I have 2 Spring App.
App A will have a controller that will receive a video file ad Multipart file and sending the file to App B via rest template.
Some Code from App A that handle sending request to App B.
#RestController
public class AppAController {
#Autowired
private final AppBService service;
#PostMapping("/sendToB")
public ResponseEntity<String> contoller(#RequestParam("file") MultipartFile file) {
String result = service.sendToB(file);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
#Service
public class AppBService {
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", file);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
}
And for App B, it will receive a video and extracting key frames from a video using JavaCV.
#RestController
public class ProcessorController {
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestParam("file") MultipartFile file) {
try {
File result = FramesExtractor.grabFrames(file);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public class FramesExtractor {
private static final Logger LOG = LoggerFactory.getLogger(FramesExtractor.class);
private FramesExtractor() {
}
public static File grabFrames(MultipartFile video) throws IOException {
LOG.info("Extracting Frames from the video " + video.getName());
String directoryName = video.getOriginalFilename() + "-frames-result";
directoryAssurance(directoryName);
try (FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(video.getInputStream());
Java2DFrameConverter converter = new Java2DFrameConverter()) {
frameGrabber.setImageWidth(480);
frameGrabber.setImageHeight(360);
frameGrabber.start();
Frame frame;
int i = 0;
while ((frame = frameGrabber.grabKeyFrame()) != null) {
BufferedImage bi = converter.getBufferedImage(frame);
ImageIO.write(bi, "png", new File(directoryName + "/" + String.format("%03d", i) + ".png"));
i++;
}
frameGrabber.stop();
LOG.info("Finish Extracting Frames");
return new File(directoryName);
} catch (Exception e) {
LOG.error(e.getMessage());
throw e;
}
}
}
Both app A and B have these properties set to their application.properties files.
server.port={There respective port}
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
And when i testing the application by using PostMan to send post request with video file in form-data body to App A /sendToB api i receive this error message.
"Type definition error: [simple type, class java.io.FileDescriptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile[\"inputStream\"]->java.io.FileInputStream[\"fd\"])"
So I want to ask what cause this problems and how I can fix it or sending video file to other Spring App running on other port properly.
I edited my previous answer because you can actually get parameters from a response body in a POST with #RequestParam annotation.
I would use JSON as content type. This is my solution:
public String sendToB(MultipartFile file) throws ResponseStatusException {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
byte[] bytesFile = file.getBytes();
String base64String = Base64.getEncoder().encodeToString(bytesFile);
Map<String, Object> body = new HashMap<>();
body.put("file", base64String);
body.put("directoryName", file.getOriginalFilename());
body.put("videoName", file.getName())
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String serverUrl = "http://127.0.0.1:8090/makeFrames";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(
serverUrl,
HttpMethod.POST,
requestEntity,
String.class
);
if (response.getStatusCode() != HttpStatus.OK) {
throw new ResponseStatusException(response.getStatusCode(), response.getBody());
}
return response.getBody();
}
For App B, the code would be:
#PostMapping("/makeFrames")
public ResponseEntity<String> framesExtractorController(#RequestBody Map<String, Object> requestBody) {
try {
String base64String = requestBody.get("file").toString();
byte[] bytesFile = Base64.getDecoder().decode(base64String);
InputStream inputStream = new ByteArrayInputStream(bytesFile);
String directoryName = requestBody.get("directoryName").toString();
String videoName = requestBody.get("videoName").toString();
File result = FramesExtractor.grabFrames(inputStream, directoryName, videoName);
return new ResponseEntity<>(result.getAbsolutePath(), HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
public static File grabFrames(InputStream inputStream, String directoryName, String videoName) throws IOException {
//Your code
}
File fileJson = new File("answer.json");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
**body.add("answer", fileJson);**
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String urlFinal = "url";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(urlFinal, requestEntity, String.class);
System.out.println(response);
The server returns me a 400 error saying that the file was not sent in the body. I was wondering if the problem is with my code or with the server.
The file is a JSON, which must be sent in Multipart-Form-data.
I left the urlFinal string with just "url" to put as an example, but there is a valid url, as I've already done tests.
You need add filename and BAOS to MultiValueMap body, add this:
File fileJson = new File("answer.json");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("filename", fileJson.getName());
body.add("file", new ByteArrayResource(Files.readAllBytes(fileJson.toPath()));
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String urlFinal = "url";
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.postForEntity(urlFinal, requestEntity, String.class);
System.out.println(response);
But is not the best mode, because you can change your code for use this method:
#Service
public class FileUploadService {
private RestTemplate restTemplate;
#Autowired
public FileUploadService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public void postFile(String filename, byte[] someByteArray) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
// This nested HttpEntiy is important to create the correct
// Content-Disposition entry with metadata "name" and "filename"
MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition contentDisposition = ContentDisposition
.builder("form-data")
.name("file")
.filename(filename)
.build();
fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
HttpEntity<byte[]> fileEntity = new HttpEntity<>(someByteArray, fileMap);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", fileEntity);
HttpEntity<MultiValueMap<String, Object>> requestEntity =
new HttpEntity<>(body, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
"/urlToPostTo",
HttpMethod.POST,
requestEntity,
String.class);
} catch (HttpClientErrorException e) {
e.printStackTrace();
}
}
}
I am trying to upload a file on my server to another server. While doing this
I am getting this error : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present]
I have put the 'file' key in my body. I am unable to figure out why this is hapenning.
Uploading Server:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body
= new LinkedMultiValueMap<>();
body.add("file", Paths.get("pathToFile").toFile());
HttpEntity<MultiValueMap<String, Object>> requestEntity
= new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate
.postForEntity(url, requestEntity, String.class);
Server that we are uploading to:
#RequestMapping(value = "/api", method = RequestMethod.POST, consumes = MediaType.ALL_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResponseDTO> solveUsingJar(#RequestParam("file") MultipartFile file) {
}
I share some code to upload a file one server to another server.
STEP-1 : Upload a file in Server-1 :
#PostMapping("/upload/profile")
public UploadFileResponse uploadProfileImg(#RequestPart("file") MultipartFile file)
{
return fileService.uploadProfileImg(file);
}
STEP-2 : Upload that file in Server-2 using RestTemplate.
public String upload(MultipartFile file) throws IOException
{
ResponseEntity<UploadFileResponse> response = null;
try
{
File convFile = new File(file.getOriginalFilename());
convFile.createNewFile();
FileOutputStream fos = new FileOutputStream(convFile);
fos.write(file.getBytes());
fos.close();
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
String uploadURI = KeyConstant.FILE_UPLOAD_API_URI+"/file"; // Server-2(file Directory) FILE_UPLOAD_API_URI : http://27.34.2.33:28181/upload/file
map.add("file", new FileSystemResource(convFile));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(uploadURI, HttpMethod.POST, requestEntity, UploadFileResponse.class);
}
catch(Exception e) {e.printStackTrace();}
return response.getBody();
}
STEP-3 : Server-2 File Upload Rest Controller :
#PostMapping("/upload/file")
public UploadFileResponse uploadFileByDirectory(#RequestPart(value = "file") MultipartFile file)
{
return this.amazonClient.uploadFilebyDirectory(KeyConstant.AMAZON_S3_PATH, file);
}
I'm trying to consume a web service that accepts a CommonsMultipartFile in the request. So, I created an HTTP client using Spring's RestTemplate. Below is the method that takes in URI and a MultipartFile as parameters. I'm trying to pass this file to the web service in the form of ByteArrayResource.
public String upload(String uri, MultipartFile file) throws IOException {
logger.info("URI: " + uri);
ByteArrayResource fileAsResource = new ByteArrayResource(file.getBytes()) {
#Override
public String getFilename() {
return file.getOriginalFilename();
}
};
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", fileAsResource);
parts.add("fileName", file.getOriginalFilename());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<MultiValueMap<String, Object>>(parts, httpHeaders);
ResponseEntity<String> responseEntity = rest.exchange(uri, HttpMethod.POST, requestEntity, String.class);
this.setStatus(responseEntity.getStatusCode());
return responseEntity.getBody();
}
This is how I'm creating a CommonsMultipartFile:
private MultipartFile getCommonsMultipartFile() throws FileNotFoundException, IOException {
File file = new File("C:\\Dummy_Test.txt");
DiskFileItemFactory factory = new DiskFileItemFactory();
FileItem fileItem = factory.createItem( "file", "multipart/form-data", false, "Dummy_Test.txt" );
IOUtils.copy(new FileInputStream(file), fileItem.getOutputStream());
MultipartFile commonsMultipartFile = new CommonsMultipartFile(fileItem);
return commonsMultipartFile;
}
But whenever I run this client to hit the web service I keep getting this error.
org.springframework.web.client.ResourceAccessException: I/O error: resource loaded from byte array cannot be resolved to absolute file path; nested exception is java.io.FileNotFoundException: resource loaded from byte array cannot be resolved to absolute file path
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:453)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:377)
at com.attikala.service.UploaderService.upload(UploaderService.java:118)
at com.attikala.service.UploaderService.main(UploaderService.java:55)
Caused by: java.io.FileNotFoundException: resource loaded from byte array cannot be resolved to absolute file path
at org.springframework.core.io.AbstractResource.getFile(AbstractResource.java:107)
at org.springframework.core.io.AbstractResource.contentLength(AbstractResource.java:116)
at org.springframework.http.converter.ResourceHttpMessageConverter.getContentLength(ResourceHttpMessageConverter.java:99)
at org.springframework.http.converter.ResourceHttpMessageConverter.write(ResourceHttpMessageConverter.java:81)
at org.springframework.http.converter.ResourceHttpMessageConverter.write(ResourceHttpMessageConverter.java:1)
at org.springframework.http.converter.FormHttpMessageConverter.writePart(FormHttpMessageConverter.java:288)
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)
... 4 more
Can someone help me in figuring out what's happening here?
Note: If I use the below code to upload the file, it works perfectly fine.
public String upload(String uri) {
LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
FileSystemResource value = new FileSystemResource(new File("C:\\Dummy_Test.txt"));
map.add("file", value);
map.add("fileName", "Dummy_Test.txt");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.exchange(uri, HttpMethod.POST, requestEntity, String.class);
return responseEntity.getBody();
}
So, I'm thinking, do I need to provide absolute path always for the file I'm uploading? I know I'm missing something here. Don't know what.
Thanks.
Finally, I found what's happening.
Here I go -
when this statement
ResponseEntity<String> responseEntity = rest.exchange(uri, HttpMethod.POST, requestEntity, String.class);
gets executed, behind the scenes it's trying to extract the file of type java.io.File from the MultipartFile passed, and then get the file length. But MultipartFile is not of that type and as a result it was throwing an exception.
To fix that I had to also override contentLength() method when creating an instance of ByteArrayResource. Ta-da!
ByteArrayResource fileAsResource = new ByteArrayResource(file.getBytes()) {
#Override
public String getFilename() {
return file.getOriginalFilename();
}
#Override
public long contentLength() throws IOException {
return file.getSize();
}
};
Hope this helps if anyone runs into the same problem.
The accepted answer did not work. In my case I had to override the getFile(), see my solution below.
registry.addResourceHandler("/my-app/user-tracking-script.js")
.setCachePeriod(0)
.resourceChain(false)
.addResolver(new ResourceResolver() {
#Override
public Resource resolveResource(final HttpServletRequest request, final String requestPath, final List<? extends Resource> locations, final ResourceResolverChain chain) {
try {
final HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
final HttpEntity<String> entity = new HttpEntity<>(headers);
// RELEVANT PART BELOW
final ResponseEntity<byte[]> response = restTemplate.exchange(userTrackingScript, HttpMethod.GET, entity, byte[].class);
final ByteArrayResource fileAsResource = new ByteArrayResource(response.getBody()) {
#Override
public File getFile() throws IOException {
final File tempFile = File.createTempFile("user-tracking", ".js");
try (final FileOutputStream out = new FileOutputStream(tempFile)) {
IOUtils.copy(new ByteArrayInputStream(response.getBody()), out);
}
return tempFile;
}
};
return fileAsResource;
} catch (final Exception e) {
log.error("Could not download user-tracking-script.js for URL: {}", userTrackingScript, e);
}
return null;
}
#Override
public String resolveUrlPath(final String resourcePath, final List<? extends Resource> locations, final ResourceResolverChain chain) {
log.error("Unexpected call to resolveUrlPath by {}", resourcePath);
return null;
}
})
;