How can I download json file from S3 using Spring Boot? - java

I have a folder in S3 bucket that contains json files. I am using Spring Boot. The user provides folder name and specific file(json file) name.
public ResponseEntity<?> downloading(String folderName, String fileName) throws IOException {
S3Object s3Object = s3client
.getObject(new GetObjectRequest(s3BucketName, folderName + fileName));
if (s3Object.getKey().length() > 0) {
//enables the user to dowload json file
//return the object that can be dowloaded, status code
return new ResponseEntity<>( HttpStatus.OK);
}
else{
//return error message and status code
return new ResponseEntity<>( HttpStatus.NOT_FOUND);
}
}

I have referred the answer from here.
So, I have added just a part of the code that will help you :
public ResponseEntity<byte[]> downloading(String folderName, String fileName) {
S3Object s3Object = s3client
.getObject(new GetObjectRequest(s3BucketName, folderName + fileName));
if (s3Object.getKey().length() > 0) {
S3ObjectInputStream input = s3Object.getObjectContent();
byte[] bytes = IOUtils.toByteArray(input);
String file = URLEncoder.encode(s3Object.getKey(), "UTF-8").replaceAll("\\+", "%20");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(bytes.length);
headers.setContentDispositionFormData("attachment", file);
return new ResponseEntity<byte[]>(bytes, HttpStatus.OK);
}
return new ResponseEntity<byte[]>(null, HttpStatus.NOT_FOUND);
}
Thanks. :)

Related

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
}

Upload a MultipartFile to a Oracle ObjectStorage

I have a Rest enpoint that accept a list of MultipartFile like this:
#CrossOrigin(origins = "*", methods = {RequestMethod.POST})
#PostMapping(value = "/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
ResponseEntity<UploadFileResponse> uploadDocuments(#RequestPart("files") List<MultipartFile> files) throws InfoNotFoundException, IOException {
log.info("Upload Documents controller");
if (files == null || files.isEmpty()) {
throw new RuntimeException("You must select at least one file for uploading");
}
UploadFileResponse uploadFileResponse = service.uploadFiles(files);
ResponseEntity<UploadFileResponse> response = new ResponseEntity<UploadFileResponse>(uploadFileResponse, HttpStatus.OK);
return response;
}
This is calling a service that has call a method inside to save the information in a ObjectStorage from oracle, like this:
public void upload(MultipartFile file) throws Exception {
HSAClientConfiguration conf = new HSAClientConfiguration();
UploadObject app = new UploadObject();
String fileName = "oci/oci_api_key.pem";
InputStream is = app.getFileFromResourceAsStream(fileName);
String result = IOUtils.toString(is, StandardCharsets.UTF_8);
log.info("Authenticating...");
AuthenticationDetailsProvider authenticationDetailsProvider =
SimpleAuthenticationDetailsProvider.builder()
.tenantId(conf.getTenantId())
.userId(conf.getUserId())
.fingerprint(conf.getFingerprint())
.privateKeySupplier(new StringPrivateKeySupplier(result))
.build();
ObjectStorage client = new ObjectStorageClient(authenticationDetailsProvider);
client.setRegion(Region.EU_FRANKFURT_1);
//Construccion del nombre del archivo.
String fecha = DateUtil.convertToYYYYMM(Timestamp.from(Instant.now()));
String objectName = fecha + "/" + file.getOriginalFilename();
log.info("Loading the file to Object Storage with name: " + objectName);
//Convertir el fichero para pasar en el putObject
InputStream inputStream = file.getInputStream();
log.info("Creating the source object to send");
PutObjectRequest putObjectRequest =
PutObjectRequest.builder()
.namespaceName(conf.getNameSpace())
.bucketName(conf.getBucket())
.objectName(objectName)
.contentLength(file.getSize())
.putObjectBody(inputStream)
.build();
client.putObject(putObjectRequest);
PutObjectResponse putObjectResponse = client.putObject(putObjectRequest);
System.out.println("Response: " + putObjectRequest);
}
If instead of Multipart, I pass to this code the Inputstream of a file in the system, it will be save correctly. But when I use the MultipartFile, I recevie an error from the RestClient like this:
com.oracle.bmc.http.internal.RestClient : Error calling available on the stream to get the available number of bytes
Look like this is produce in the clases from oracle that try to serialize the object. I don't know why. Does anyone know how to serialize a InputStream or deal with this?
Thanks
It was a stupid problem with the response that try to serialize the InputStream. If you remove PutObjectResponse putObjectResponse = client.putObject(putObjectRequest); The code works smooth.
Enjoy it!

How to download excel file in spring boot using HttpHeaders?

I am getting a resulting file but in the response I am getting gibberish symbols
here is the code I am trying
public ResponseEntity<InputStreamResource> getExcel(String filePath) throws Exception {
try {
Path excelPath = Paths.get(filePath);
byte[] excel = Files.readAllBytes(excelPath);
ByteArrayInputStream excelToByte = new ByteArrayInputStream(excel);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.add("Content-Disposition", "attachment; filename=ABCGeneratedExcel.xls");
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(new InputStreamResource(excelToByte));
}
catch (NoSuchFileException e) {
System.out.prinln("does not exist");
}
You should use HttpServletResponse instead. And let Spring framework initialize it by declaring as Controller method's parameter. Because you will write the excel file as binary stream, do not define the return type.
Then write the response stream after setting the contentType and header for excel downloading.
public void getExcel(String filePath, HttpServletResponse response) {
byte[] excel = Files.readAllBytes(excelPath);
String fileName = "anyFileName.xlsx"
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.getWriter().write(excel); // in fact, you need to surround this by try-catch block
}
Path filePath = pathToFolder.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return resource;
} else {
throw new NotFoundException(String.format("File %s not found", fileName));
}
Where path to File - in your directory, and file name - name of file in your directory.
Next step is:
Resource resource = service.downloadFile(fileName);
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException e) {
log.info("Could not determine file type");
}
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
return ResponseEntity
.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, String.format(
"%s; filename=%s", content.name().toLowerCase(), resource.getFilename()
)
)
.body(resource);
Where first %s - attachment - for downloading, and inline - for rendering file in the browser.
Second %s - name of file (note that if you are storing your file in the file system, use file name with extension).

Provide JSON response and download file simultaneously with Spring-Boot

Requirement:
I need to create a Rest API which can allows to download a file as well as a JSON response.
I already have 2 different APIs to solve the purpose, but now I need to merge these APIs to a single one.
public ResponseEntity<InputStreamResource> downloadFile1(
#RequestParam(defaultValue = DEFAULT_FILE_NAME) String fileName) throws IOException {
MediaType mediaType = MediaTypeUtils.getMediaTypeForFileName(this.servletContext, fileName);
System.out.println("fileName: " + fileName);
System.out.println("mediaType: " + mediaType);
File file = new File(DIRECTORY + "/" + fileName);
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
// Content-Disposition
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
// Content-Type
.contentType(mediaType)
// Contet-Length
.contentLength(file.length()) //
.body(resource);
}
Above is the existing code that only return a file to download but I need a json response as well.
You need to return Multipart content. See for example
https://github.com/juazugas/spring-boot-multipart/blob/master/src/main/java/com/example/demo/server/MultiEndpoint.java
The code
#GET
#Produces("multipart/mixed")
public MultipartBody getMulti2(#QueryParam("name") String name) {
List<Attachment> attachments = new LinkedList<>();
attachments.add(new Attachment("root", "application/json", service.getEntity(name)));
attachments.add(new Attachment("image", "application/octet-stream", service.getEntityData(name)));
return new MultipartBody(attachments, true);
}

Spring Rest - Posting Files

I have the following code which I use for posting a file to a service and it works fine.
The only problem I have, is that I have to write a temporary file to get a FileSystemResource for posting the object with the restTemplate
Is there anyway I can adapt the following code so that I dont have to write a temporary file?
public String postNewIcon2(Integer fileId, MultipartFile multiPartfile) {
LOG.info("Entered postNewIcon");
Map<String, Object> params = getParamsWithAppKey();
params.put("fileId", fileId);
String result = null;
File tempFile = null;
try {
String originalFileNameAndExtension = multiPartfile.getOriginalFilename();
String tempFileName = "c:\\temp\\image";
String tempFileExtensionPlusDot = ".png";
tempFile = File.createTempFile(tempFileName, tempFileExtensionPlusDot);
multiPartfile.transferTo(tempFile);
FileSystemResource fileSystemResource = new FileSystemResource(tempFile);
// URL Parameters
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", fileSystemResource);
// Post
result = restTemplate.postForObject(getFullURLAppKey(URL_POST_NEW_ICON), parts, String.class, params);
} catch (RestClientException restClientException) {
System.out.println(restClientException);
} catch (IOException ioException) {
System.out.println(ioException);
} finally {
if (tempFile != null) {
boolean deleteTempFileResult = tempFile.delete();
LOG.info("deleteTempFileResult: {}", deleteTempFileResult);
}
}
return result;
}
Thank you
Answer with help with Kresimir Nesek and this link
Sending Multipart File as POST parameters with RestTemplate requests
The following code did the trick - no need for a temporary file now
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
final String filename="somefile.txt";
map.add("name", filename);
map.add("filename", filename);
ByteArrayResource contentsAsResource = new ByteArrayResource(content.getBytes("UTF-8")){
#Override
public String getFilename(){
return filename;
}
};
map.add("file", contentsAsResource);
String result = restTemplate.postForObject(urlForFacade, map, String.class);
MultipartFile needs to have some temp location.
Please try this code, to get physical file:
private File getTempFile(MultipartFile attachment){
CommonsMultipartFile commonsMultipartFile = (CommonsMultipartFile) attachment;
DiskFileItem diskFileItem = (DiskFileItem) commonsMultipartFile.getFileItem();
return diskFileItem.getStoreLocation();
}

Categories

Resources