How to create a pdf file and send email with Spring boot - java

I have a controller and an Exporter class to create pdf of one class data in Spring boot. It works at localhost. And I can send emails with attachments which are in the resources/static/ directory from this link:
https://asbnotebook.com/2020/01/26/send-email-with-attachment-spring-boot/
I want to email the pdf file created at fly. I tried to combine them but it didnt work.
public String sendMail(EmailRequestDto request, Map<String, String> model) {
String response;
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
Template template = configuration.getTemplate("email.ftl");
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
helper.setTo(request.getTo());
helper.setFrom(request.getFrom());
helper.setSubject(request.getSubject());
helper.setText(html, true);
List<PurchaseDetail> cities = (List<PurchaseDetail>)basketService.getPurchases();
ByteArrayInputStream bis = InvoicePdfExporter.citiesReport(cities);
HttpHeaders headers = new HttpHeaders(); headers.add("ContentDisposition",
"inline; filename=citiesreport.pdf");
InputStreamResource rs= (new InputStreamResource(bis)) ;
//this gives error because rs is a inputStreamResource but not InputStream
helper.addAttachment("citiesreport.pdf",newByteArrayResource(IOUtils.toByteArray(rs)));
mailSender.send(message);
response = "Email has been sent to :" + request.getTo();
} catch (MessagingException | IOException | TemplateException e) {
response = "Email send failure to :" + request.getTo();
}
return response;
}
And my working controller class which creates pdf. InvoicePdfExporter class adds datatable to document and returns as return new ByteArrayInputStream(out.toByteArray());:
#RequestMapping(value = "/pdfreport", method = RequestMethod.GET,
produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<InputStreamResource> citiesReport() throws IOException
{
List<PurchaseDetail> purchases = (List<PurchaseDetail>)
basketService.getPurchases();
ByteArrayInputStream bis = InvoicePdfExporter.citiesReport(purchases);
HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition",
"inline; filename=citiesreport.pdf");
return
ResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_PDF)
.body(new InputStreamResource(bis)) ; }
}
I really need help I really dont understand from IOStreams, I tried many things but none of them solved my problem. Thanks!!
Edit:
I solved this problem by changing the return type of my InvoicePdfExporter to InputStreamSource and changed to this:
List<PurchaseDetail> cities = (List<PurchaseDetail>)basketService.getPurchases();
InputStreamSource bis =InvoicePdfExporter.citiesReport(cities);
HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition",
"inline; filename=citiesreport.pdf");
helper.addAttachment("citiesreport.pdf",bis, "application/pdf" );

Related

Spring not generating Content Type

I'm using SpringBoot 3.0.1 and I'm trying to get a file stored in the backend using Axios.
The controller is the following:
#GetMapping(value = "/api/files/{fileName}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> getFile(final #PathVariable("fileName") String fileName) {
try {
Path filePath = Path.of(fileName);
File file = filePath.toFile();
HttpHeaders responseHeaders = new HttpHeaders();
String filename = filePath.getFileName().toString();
responseHeaders
.setContentDisposition(ContentDisposition.builder("attachment")
.filename(filename, StandardCharsets.UTF_8)
.build());
FileSystemResource fileSystemResource = new FileSystemResource(file);
return ResponseEntity
.ok()
.headers(responseHeaders)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(file.length())
.lastModified(file.lastModified())
.body(fileSystemResource);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
When I get the answer (status is 200), the header I've set in the controller is not given. In particular, the Content-Disposition header is not defined in the answer.
I'm wondering if there is any missing configuration that must be set in Sprint Boot in order to be allowed to set a custom header. Anyone who knows what can cause this and how to fix it?

file download via http post is returning the zip file contents

I could see many related topics, but I have a specific problem. I am using spring boot controller to download a zip file. I am able to download the file when it is http verb get, but as I have to pass a big json payload I changed to post. Since then instead of downloading it as file it is responding the contents of the file with some ascii characters. Below is the method in controller for downloading the file.
#ApiResponses(value = { #ApiResponse(code = 404, message = "file could not be found"),
#ApiResponse(code = 200, message = "File was created sucessfully") })
#PostMapping(path="/download-file/1.0", produces="application/zip")
public ResponseEntity<InputStreamResource> downloadFile(
#ApiParam(value = "File creation contents", required = true) #RequestBody InputDetailsVO inputDetailsVO) {
File file = null;
InputStreamResource resource = null;
HttpHeaders headers = new HttpHeaders();
try {
//Creating InputStreamResource out of zip file
resource = new InputStreamResource(new FileInputStream(file));
String contentType = "application/zip";
if (!StringUtils.isEmpty(contentType)) {
headers.setContentType(MediaType.parseMediaType(contentType));
}
headers.add("Content-Disposition","attachment; filename=\""+file.getName()+"\"");
} catch (Exception e) {
log.error("Issue with file creation",e);
}
return ResponseEntity.ok()
.contentLength(file.length())
.contentType(MediaType
.parseMediaType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.headers(headers).body(resource);
}
Below is the response I am getting instead of file download
PK;��N <?xml version="1.0" encoding="UTF-8"?>
<employeeDetails>
<name>Harry</name>
<age>30</30>
<email>test#test.com</test>
</employeeDetails>PK�qB�#Y;YPK;��N�qB�#Y;Yemployee details.xmlPKL�Y
Try like this, you can download any type of file. I assume that InputDetailsVO contains the name of the file or you can have your own logic to pick the file name. On the top of this method, you can provide swagger related annotations.
#PostMapping(value = "/download-file/1.0", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> downloadFile(#RequestBody InputDetailsVO inputDetailsVO) {
String dirPath = "your-location-path";
byte[] fileBytes = null;
try {
String fileName = inputDetailsVO.getFileName();
fileBytes = Files.readAllBytes(Paths.get(dirPath + fileName));
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(fileBytes);
}
I also had a similar use case. I am sharing the code which had solved the issue.
#RequestMapping(value="/download",method=RequestMethod.GET,produces="application/zip" )
public ResponseEntity<?> download(HttpServletResponse response) throws IOException
{
//Some Code...
File file = new File("F:\\Folder\\Folder\\Folder\\"+filename);
InputStreamResource resource2 = new InputStreamResource(new FileInputStream(file));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", String.format("inline; filename=\"" + filename + "\""));
response.setHeader("responseType", "arraybuffer");
response.setHeader("Content-Length", ""+file.length());
return new ResponseEntity<InputStreamResource>(resource2,HttpStatus.ACCEPTED);
}

Download file via Spring Web

Currently, this is my code in the controller to return a XLS file for download:
#RequestMapping(value = "/export-data/", method = RequestMethod.GET)
public ResponseEntity exportAllData() {
ResponseEntity respEntity = null;
SheetDownload sheetDownload = new SheetDownload();
try {
ByteArrayOutputStream result = sheetDownload.createMentoringSheet();
HttpHeaders responseHeaders = new HttpHeaders();
byte[] out = result.toByteArray();
responseHeaders.add("content-disposition", "attachment; filename=export-data.xlsx");
responseHeaders.add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
respEntity = new ResponseEntity(out, responseHeaders,HttpStatus.OK);
}catch(Exception e){
respEntity = new ResponseEntity ("File Not Found", HttpStatus.OK);
}
return respEntity;
}
When I go to the "network" in Chrome, all I see in the response is:
So, there's no trigger to browser download, for example. The goal is to return the file to be downloaded in the proper format (XLSX).
Can someone help me?
Thank you in advance.

Image cannot be displayed in spring application

I have write a simple Spring + Angular application just for learn more about it.
I have a spring controller which is mapped to a URL and when an request comes it returns an image.
I have written all the codes and the spring controller returns me the image but when i set it in the HTML it is not displayed correctly
here is my spring controller
#RequestMapping(value = "image/", method = RequestMethod.GET)
public ResponseEntity<byte[]> getChequeImage(HttpSessionsession,#PathVariable("itemId") Integer itemId,
HttpServletResponse response) {
try{
InputStream in = new FileInputStream(new File("path_to_image.jpg"));
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.OK);
}catch (IOException e){
LOGGER.error(e);
e.getMessage(), response);
return null;
}
}
here is my HTML code
<img src="{{image}}"/>
image is an Angular variable. Angular service is sending the request and binding the data to the image variable
here is the angular code
#scope.image = "data:image/jpg," + data_from_the_api;
You can't use raw image bytes directly on the page, but you can do Base64 encoding, this would be the adaptations
#RequestMapping(value = "image/", method = RequestMethod.GET)
public ResponseEntity<String> getChequeImage(HttpSessionsession,#PathVariable("itemId") Integer itemId,
HttpServletResponse response) {
try{
InputStream in = new FileInputStream(new File("path_to_image.jpg"));
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_JPEG);
byte[] binaryData = IOUtils.toByteArray(in)
byte[] encodeBase64 = Base64.encodeBase64(binaryData);
String base64Encoded = new String(encodeBase64, "UTF-8");
return new ResponseEntity<String>(base64Encoded , headers, HttpStatus.OK);
}catch (IOException e){
LOGGER.error(e);
e.getMessage(), response);
return null;
}
}
and as TechMa9iac said in the comment you should set #scope.image = "data:image/jpg;base64," + data_from_the_api;

How to set 'Content-Disposition' and 'Filename' when using FileSystemResource to force a file download file?

What is the most appropriate, and standard, way to set the Content-Disposition=attachment and filename=xyz.zip using Spring 3 FileSystemResource?
The action looks like :
#ResponseBody
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET, produces = "application/zip")
#PreAuthorize("#authorizationService.authorizeMethod()")
public FileSystemResource doAction(#PathVariable String abcd, #PathVariable String efgh) {
File zipFile = service.getFile(abcd, efgh);
return new FileSystemResource(zipFile);
}
Although the file is a zip file so the browser always downloads the file, but I would like to explicitly mention the file as attachment, and also provide a filename that has nothing to do with the files actual name.
There might be workarounds for this problem, but I would like to know the proper Spring and FileSystemResource way to achieve this goal.
P.S. The file that is being used here is a temporary file, marked for deletion when the JVM exists.
In addition to the accepted answer, Spring has the class ContentDisposition specific for this purpose. I believe it deals with the file name sanitization.
ContentDisposition contentDisposition = ContentDisposition.builder("inline")
.filename("Filename")
.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentDisposition(contentDisposition);
#RequestMapping(value = "/action/{abcd}/{efgh}", method = RequestMethod.GET)
#PreAuthorize("#authorizationService.authorizeMethod(#id)")
public HttpEntity<byte[]> doAction(#PathVariable ObjectType obj, #PathVariable Date date, HttpServletResponse response) throws IOException {
ZipFileType zipFile = service.getFile(obj1.getId(), date);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
response.setHeader("Content-Disposition", "attachment; filename=" + zipFile.getFileName());
return new HttpEntity<byte[]>(zipFile.getByteArray(), headers);
}
#RequestMapping(value = "/files/{file_name}", method = RequestMethod.GET)
#ResponseBody
public FileSystemResource getFile(#PathVariable("file_name") String fileName,HttpServletResponse response) {
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=somefile.pdf");
return new FileSystemResource(new File("file full path"));
}
Here is an alternative approach for Spring 4. Note that this example clearly does not use good practices regarding filesystem access, this is just to demonstrate how some properties can be set declaratively.
#RequestMapping(value = "/{resourceIdentifier}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
// #ResponseBody // Needed for #Controller but not for #RestController.
public ResponseEntity<InputStreamResource> download(#PathVariable(name = "resourceIdentifier") final String filename) throws Exception
{
final String resourceName = filename + ".dat";
final File iFile = new File("/some/folder", resourceName);
final long resourceLength = iFile.length();
final long lastModified = iFile.lastModified();
final InputStream resource = new FileInputStream(iFile);
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" + resourceName)
.contentLength(resourceLength)
.lastModified(lastModified)
.contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)
.body(resource);
}
Made few changes to both given answers and I ended up with the best of both in my project where I needed to extract an image from the database as a blob and then serve it to the clients :
#GetMapping("/images/{imageId:.+}")
#ResponseBody
public ResponseEntity<FileSystemResource> serveFile(#PathVariable #Valid String imageId,HttpServletResponse response)
{
ImageEntity singleImageInfo=db.storage.StorageService.getImage(imageId);
if(singleImageInfo==null)
{
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
Blob image=singleImageInfo.getImage();
try {
String filename= UsersExtra.GenerateSession()+"xxyy"+singleImageInfo.getImage1Ext().trim();
byte [] array = image.getBytes( 1, ( int ) image.length() );
File file = File.createTempFile(UsersExtra.GenerateSession()+"xxyy", singleImageInfo.getImage1Ext().trim(), new File("."));
FileOutputStream out = new FileOutputStream( file );
out.write( array );
out.close();
FileSystemResource testing=new FileSystemResource(file);
String mimeType = "image/"+singleImageInfo.getImage1Ext().trim().toLowerCase().replace(".", "");
response.setContentType(mimeType);
String headerKey = "Content-Disposition";
String headerValue = String.format("attachment; filename=\"%s\"", filename);
response.setHeader(headerKey, headerValue);
// return new FileSystemResource(file);
return ResponseEntity.status(HttpStatus.OK).body( new FileSystemResource(file));
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
Using a ResponseEntity in Kumar's code will help you respond with the correct Response code.
Note: converting from a blob to a file is quoted from this link:
Snippet to create a file from the contents of a blob in Java

Categories