How to upload file to a spring mvc RestController using post method - java

I'm having the follwing controller:
#RequestMapping(value = "/uploadCert", method = RequestMethod.POST)
#ResponseBody
public String uploadCert( #RequestParam(value = "file", required = false) List<MultipartFile> files){
for (MultipartFile file : files) {
String path = "tempFolder";
File targetFile = new File(path);
try {
FileUtils.copyInputStreamToFile(file.getInputStream(), targetFile);
} catch (IOException e) {
e.printStackTrace();
}
}
return "done";
}
Tested by the following unit test:
#Test
public void uploadCertFile() throws Exception {
//given
MockMultipartFile file = new MockMultipartFile("file", "test.txt", "text/plain", "bar".getBytes());
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.fileUpload("/uploadCert").file(file);
System.out.println(request);
//when
ResultActions resultActions = mockMvc.perform(request);
//then
MvcResult mvcResult = resultActions.andReturn();
assertEquals(200, mvcResult.getResponse().getStatus());
File savedFile = new File("./test.txt");
assertTrue(savedFile.exists());
savedFile.delete();
}
which it workign good, i have another server in java code that would need to upload files into the above controller. I was trying to look for parallel of MockMVCRequestMapping to be use in the code but could not find it, what should i use insted to send this web request from a java code?

You can do something like this:
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public ResponseEntity<String> uploadDocument(MultipartHttpServletRequest request, HttpServletResponse response) throws IOException {
Iterator<String> iterator = request.getFileNames();
MultipartFile multipartFile = null;
while (iterator.hasNext()) {
multipartFile = request.getFile(iterator.next());
final String fileName = multipartFile.getOriginalFilename();
final String fileSize = String.valueOf(multipartFile.getSize());
//do rest here
}
}

Related

Why Multipart form "restream" from gateway microservice isn't working and attached file isn't resent?

I have a controller in gateway microservice that accepts the MultipartFile and resends to the service behind it
#PostMapping
public ResponseEntity upload(#ApiParam(name = "file", value = "File", required = true) MultipartFile file)
throws BaseException {
if (Objects.isNull(file)){
throw new CheckFieldException("file", MultipartFile.class);
}
if (megabyte * maxFileSize - file.getSize() < 0){
return ResponseEntity.accepted().body(new DocumentResponseDTO(false, "File size exceeds " + maxFileSize + "MB"));
}
DiscoveryConfig.CashTracking config = discoveryConfig.getCashTracking();
UriComponents uriStatementUpload = UriComponentsBuilder.newInstance().scheme(config.getScheme())
.host(config.getHost()).port(config.getPort()).path(config.getExcelNominalOperationsPath()).build(true);
try {
HttpEntity<byte[]> fileEntity = new HttpEntity(file.getBytes());
ResponseEntity<DocumentResponseDTO> entity = restTemplate.postForEntity(uriStatementUpload.toUri(), fileEntity, DocumentResponseDTO.class);
return entity;
} catch (HttpClientErrorException e) {
return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsString());
} catch (IOException e) {
return ResponseEntity.status(500).body("IOException while getting bytes stream from file");
}
}
and in CashTracking service there is also file upload like that:
#PostMapping(value = "/upload")
public ResponseEntity uploadExcelNominalOperationsFile(#ApiParam(name = "file", value = "File", required = true) MultipartFile file) throws IOException {
try (InputStream is = file.getInputStream()) {
log.info("Processing incoming Excel file with nominal operations");
Workbook workbook = new XSSFWorkbook(is);
log.info("Processing workbook");
Sheet sheet = workbook.getSheetAt(0);
log.info("Processing the first sheet");
List<NominalOperationVO> nominalOperationVOs = new ArrayList<>();
List<String> fileHeaders = new ArrayList<>();
And when the file is actually uploaded to the gateway service, the service behind it starts processing the file upload, but the MultipartFile file is null. I have explicitly put it in the Entity I have sent to the service behind the gateway, the question, what I'm doing wrong if it is null? If I do upload to that microservice directly, it process the request correctly.
The main stuff I was missing was putting the Http headers per specific multipart form's parts. They should be identical to what has been sent to the gateway service.
public ResponseEntity upload(#ApiParam(name = "file", value = "Файл", required = true) MultipartFile file)
throws BaseException {
if (Objects.isNull(file)){
throw new CheckFieldException("file", MultipartFile.class);
}
if (megabyte * maxFileSize - file.getSize() < 0){
return ResponseEntity.accepted().body(new DocumentResponseDTO(false, "File size exceeds " + maxFileSize + "MB"));
}
DiscoveryConfig.CashTracking config = discoveryConfig.getCashTracking();
UriComponents uriStatementUpload = UriComponentsBuilder.newInstance().scheme(config.getScheme())
.host(config.getHost()).port(config.getPort()).path(config.getExcelNominalOperationsPath()).build(true);
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
//here is the really needed stuff with 2 headers
Resource resource = new ByteArrayResource(file.getBytes());
multipartBodyBuilder.part("file", resource)
.header("Content-Type",file.getContentType())
.header("Content-Disposition","form-data; name=\"file\"; filename=\""+file.getOriginalFilename()+"\"");
// multipart/form-data request body
MultiValueMap<String, HttpEntity<?>> body = multipartBodyBuilder.build();
HttpEntity<MultiValueMap<String, HttpEntity<?>>> requestEntity
= new HttpEntity<>(body, headers);
ResponseEntity<DocumentResponseDTO> entity = restTemplate.postForEntity(uriStatementUpload.toUri(), requestEntity, DocumentResponseDTO.class);
return entity;
} catch (HttpClientErrorException e) {
return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsString());
} catch (IOException e) {
return ResponseEntity.status(500).body("IOException while getting bytes stream from file");
}
}

Error while sending Video File from Java Spring Microservice to other Microservice on other port using Spring Rest Template

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 upload of CommonsMultipartFile using RestTemplate is failing

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;
}
})
;

How to return base64 Image from GET request through proxy?

Here is my code. It seems to alter my base64 image incorrectly.
#RequestMapping(value = "/get/**", method = RequestMethod.GET)
public String home(Locale locale, Model model, HttpServletRequest request) {
String query = request.getRequestURI().replaceAll("/OtkProxy/get/", "");
final String uri = "http://110.25.114.11/"+query;
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
model.addAttribute("data", result);
return "home";
}
What am I doing wrong?
Managed do it that way:
#RequestMapping(value = "/getphoto/**", method = RequestMethod.GET)
public void homephoto(HttpServletResponse response, HttpServletRequest request) {
String query = request.getRequestURI().replaceAll("/OtkProxy/getphoto/", "");
//final String uri = "http://10.25.114.11/"+query;
final String uri = "http://qmatic.faceis.ru/"+query;
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
HttpEntity entity = new HttpEntity(headers);
String res = restTemplate.getForObject(uri, String.class, entity);
response.setContentType("image/jpeg");
try {
response.getOutputStream().print(res);
}
catch (Exception ex)
{
System.out.println("getphoto - " + ex.getMessage());
}
}

How can I get download location browser in spring mvc controller?

The problem is next necessary to make file on server and after sending to client and print.
#RequestMapping(value = "/some", method = RequestMethod.GET)
public void getFileForPrint(HttpServletResponse response,
#RequestParam(value = "id", required = true) Long id,
#RequestParam(value = "print", defaultValue = "false") Boolean print) throws Exception {
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fname);
ServletOutputStream out = response.getOutputStream();
somefile.write(out);
Desktop.getDesktop.print(new File("download location browser"));}
}
If you mean by when you are hitting an end point you want to download a file immediately then the following code should help you.
#RequestMapping(value = "/somePath", method = RequestMethod.GET)
#ResponseBody
public void getFileForPrint(HttpServletResponse response,
#RequestParam(value = "id", required = true) Long id,
#RequestParam(value = "print", defaultValue = "false") Boolean print) throws Exception {
String filename="nameOfFileWhenDownloaded.txt";
try {
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
OutputStreamWriter osw = new OutputStreamWriter(response.getOutputStream());
osw.write(contentsOfTheFile);
osw.flush();
osw.close();
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
throw new Exception("Error is sending file");
}
}

Categories

Resources