SpringBoot REST produces pdf - spring-boot

I want to generate a dynamic PDF from a SpringBoot REST endpoint. The below generates a file but not a PDF. Any pointers in how to get a PDF file by altering the below code. Thanks in advance.
#GetMapping(value = "/generatePDF", produces = "application/pdf")
#SneakyThrows
public ResponseEntity<InputStreamResource> generatePDF(HttpServletRequest httpRequest,
HttpServletResponse response) {
String str = "Some test data goes here...";
byte[] pdf = str.getBytes();
File file = new File("C:\\test\\test.pdf");
FileUtils.writeByteArrayToFile(file, pdf);
FileInputStream fileInputStream = new FileInputStream(file);
return ResponseEntity.ok().contentLength(file.length())
.contentType(MediaType.APPLICATION_PDF)
.header("content-disposition", "filename=report.pdf")
.body(new InputStreamResource(fileInputStream));

try this
#GetMapping(value = "/generatePDF", produces = "application/pdf")
#SneakyThrows
public ResponseEntity<byte[]> generatePDF(HttpServletRequest httpRequest, HttpServletResponse response) {
String str = "Some test data goes here...";
byte[] pdf = str.getBytes();
File file = new File("C:\\test\\test.pdf");
FileUtils.writeByteArrayToFile(file, pdf);
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
header.setContentLength(pdf.length);
header.set("Content-Disposition", "attachment; filename=" + "pdf-" + id + ".pdf");
return new ResponseEntity<>(pdf, header, HttpStatus.OK);
}

Related

ResponseEntity results in additional underscore ( _ ) in filename's extension

I have a GET mapping like below:
#RequestMapping(value = "/downloadDocumentById/{symName}/{docId}", method = RequestMethod.GET)
public ResponseEntity<FileSystemResource> downloadStuff(#PathVariable String symName, #PathVariable String docId)
throws IOException {
File newFile = null;
String fullPath = documentService.getDocumentById(symName, docId).getName();
newFile = new File(fullPath);
System.out.println("newFile_Name:" + newFile.getName());
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
respHeaders.setContentLength(newFile.length());
respHeaders.setContentDispositionFormData("attachment; filename=" + newFile.getName(), null);
return new ResponseEntity<FileSystemResource>(new FileSystemResource(newFile), respHeaders, HttpStatus.OK);
}
The sysout shows the newFile_Name as sample1.pdf but not sure why my return statement is downloading the filename as sample1.pdf_
What am I doing wrong here? Or how can I fix it?

How to download PDF from Spring REST service using Postman

I have a Spring based Rest service which gives PDF as response. With my below code I can able to get the PDF content as binary values in my postman. My problem is to download it as attachment when I call the service.
To achieve this do i need to make any change in code or in Client.
#GetMapping(value="/getUserpdf")
public ResponseEntity<Resource> getUserInfo(#RequestHeader(name="reqHeader") Map<String, String> reqHeader,
#RequestParam(name="userId",required=true) String userId){
MetaInfo metaInfo = getHeaderValues(reqHeader);
//To get Actual PDF content as Bytes
byte[] pdfBytes = getUserPdfService.getUserInfo(metaInfo,userId);
ByteArrayResource resource = new ByteArrayResource(pdfBytes);
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=UserInfo.pdf");
return ResponseEntity
.ok()
.headers(headers)
.contentLength(pdfBytes.length)
.contentType(MediaType.parseMediaType("application/octet-stream")).body(resource);
}
Also I have registered my Converter
#Bean
public HttpMessageConverters customConverters() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
return new HttpMessageConverters(arrayHttpMessageConverter);
}
Here is an example :
#GetMapping("/getUserpdf/{id}")
#CrossOrigin
#ResponseBody
public ResponseEntity<InputStreamResource> downloadFile(#PathVariable(required = true, value = "id") Long id,#RequestParam(name="userId",required=true) String userId,HttpServletRequest request) throws IOException {
//To get Actual PDF content as Bytes
byte[] pdfBytes = getUserPdfService.getUserInfo(id,userId);
if (Objects.nonNull(pdfBytes)) {
String fileName = "UserInfo.pdf";
MediaType mediaType = MediaType.parseMediaType("application/pdf");
File file = new File(fileName);
FileUtils.writeByteArrayToFile(file, pdfBytes); //org.apache.commons.io.FileUtils
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);
} else {
throw ResponseEntity.notFound().build();
}
}
NB: i am not sur about the mediaType but you can confirm if it's ok !

GridFSDBFile cannot be cast to org.springframework.web.multipart.MultipartFile

I'm coding a spring mvc webapp that uses images of type MultipartFile which i convert to byte[] and then to Inputstream and store it in MongoDB using GridFsTemplate.
Now the problem is I want to display the stored images in a webpage but whenever I try to, the database returns the image file as GridFSDBFiles and so tosses the following exception:
java.lang.ClassCastException: com.mongodb.gridfs.GridFSDBFile cannot be cast to org.springframework.web.multipart.MultipartFile
This is my DAO for storing images:
public void saveScan(Scan scan) throws IOException {
String owner = String.valueOf(scan.getPatientId());
String fileName = String.valueOf(scan.getPatientId() + "" + scan.getScanType());
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/dd/YYYY HH:mm a");
String uploadTime = simpleDateFormat.format(date);
System.out.println("the scan type is " + scan.getScanType());
DBObject metaData = new BasicDBObject();
metaData.put("owner", owner);
metaData.put("fileName", fileName);
metaData.put("uploadTime", uploadTime);
byte[] scanBytes = scan.getScan().getBytes();
InputStream inputStream = new ByteArrayInputStream(scanBytes);
scanDaoImpl.SaveScan(inputStream, fileName, "image/jpeg", metaData);
}
And this is for retrieving the images:
public MultipartFile findOneScan(BigInteger patientId) {
MultipartFile multipartFile = (MultipartFile) gridFsTemplate
.findOne(new Query(Criteria.where("metadata.owner").is(patientId)));
return multipartFile;
And this is my controller for getting images
#ResponseBody
#RequestMapping(value = "/patients/{id}/scan", produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> scanImage(#PathVariable("id") BigInteger id) throws IOException {
logger.debug("scanImage() is finding Image to display");
byte[] bs = patientScanServiceImpl.findOne(id).getBytes();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.IMAGE_JPEG);
httpHeaders.setCacheControl(CacheControl.noCache().getHeaderValue());
return new ResponseEntity<byte[]>(bs, httpHeaders, HttpStatus.OK);
}
This is my thymeleaf image tag:
<span>
<img th:src="#{/patients/{patientid}/scan(patientid=${patient.id})}" width="250" height="250"/>
</span>
Now I have more insight in this, and I have finally found a solution. You cannot directly cast a gridFSDBFile straight to byte[ ]; it must first be converted to OutputStream and then byte[ ] if it must be displayed. So I allowed my DAO method to return a GridFSDBFile but in the service layer I converted the GridFSDBFile to ByteArrayOutputStream and then to byte[ ].
Now my DAO method for retrieving images is
public GridFSDBFile findOneScan(BigInteger patientId, String scanType) {
String fileName = String.valueOf(patientId + "" + scanType);
GridFSDBFile gridFSDBFile = gridFsTemplate.findOne(new Query(Criteria.where("metadata.fileName").is(fileName)));
return gridFSDBFile;
And my service layer which feeds the controller is
public byte[] findOne(BigInteger patientId, String scanType) throws IOException {
GridFSDBFile gridFSDBFile = scanRepository.findOneScan(patientId, scanType);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
gridFSDBFile.writeTo(outputStream);
byte[] bs = outputStream.toByteArray();
return bs;
}
And the whole thing works fine.

Spring Restful Mutipart

I would like to ask information regarding Multipart/Form-data, if these are compatible with RequestMethod.GET?
In my case I have to return a file + JSON in one response. (Note: File should not be inside the JSON). Sample response:
FILE
{
"id":"1234",
"name":"question Man"
}
I think this might be helpful, please modify it as of your needs.
#RequestMapping(value = URIConstansts.GET_FILE, produces = { "application/json" }, method = RequestMethod.GET)
public #ResponseBody ResponseEntity getFile(#RequestParam(value="fileName", required=false) String fileName,HttpServletRequest request) throws IOException{
ResponseEntity respEntity = null;
byte[] reportBytes = null;
File result=new File("/filepath/"+fileName);
if(result.exists()){
InputStream inputStream = new FileInputStream("/filepath/"+fileName);
String type=result.toURL().openConnection().guessContentTypeFromName(fileName);
byte[]out=org.apache.commons.io.IOUtils.toByteArray(inputStream);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("content-disposition", "attachment; filename=" + fileName);
responseHeaders.add("Content-Type",type);
respEntity = new ResponseEntity(out, responseHeaders,HttpStatus.OK);
}else{
respEntity = new ResponseEntity ("File Not Found", HttpStatus.OK);
}
return respEntity;
}

Issue in downloading zip file in Spring REST Service

My API is as follows:
#ApiOperation(value = "Zip of all the documents the customer attached to their application (id and loan)", notes = "", response = Void.class, tags = {
"Manage Customers/Applications",
})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK", response = Void.class)
})
#RequestMapping(value = idPath + "/files/customer-documents/zip",
method = RequestMethod.GET)
#ResponseBody
void downloadCustomerDocumentsAsZip(HttpServletResponse response,
#ApiParam(value = "Application ID", required = true) #PathVariable(value = "applicationId")
Long applicationId);
The Rest Controller:
#Override
public void downloadCustomerDocumentsAsZip(HttpServletResponse response,
#ApiParam(value = "Application ID", required = true) #PathVariable(value = "applicationId")
Long applicationId) {
InputStream inputStream = new ByteArrayInputStream(manageApplicationsService.findCustomerDocumentsAsZip(applicationId));
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=zipFile.zip");
try {
FileCopyUtils.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
The Response:
PK{i�Jtemp0+I�(Q(A%
Issue:
I want to download the zip file as an attachment, but the response is as above.
Note:
I tried all the download methods which are explained on Rest Download Endpoints but none of them were successful. I also add
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
to the API definition but again no success.
So, I would be so grateful if anyone could help me with their genuine solution.
I had the same issue. Changing Content-Type to MediaType.APPLICATION_PDF_VALUE triggered download action for me. But then "Save As" dialog display filename extension as .pdf by default.
With HttpServletResponse
response.setContentType(MediaType.APPLICATION_PDF_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
response.setContentLength((int)contents.length);
try {
response.getOutputStream().write(contents);
response.getOutputStream().flush();
} catch (IOException e) {
throw new BadRequestException("Could not generate file");
}
Of if you use ResponseEntity
byte[] contents = fileContent.getBytes();
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
responseHeaders.add(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_PDF_VALUE);
return new ResponseEntity<byte[]>(contents, responseHeaders,HttpStatus.OK);
You can just return a ResponseEntity<byte[]> in your controller. Add Content-Type and Content-Disposition headers to your response so that it opens properly.
public ResponseEntity<byte[]> downloadCustomerDocumentsAsZip(
#ApiParam(value = "Application ID", required = true)
#PathVariable(value = "applicationId") Long applicationId) {
byte[] bytes = manageApplicationsService.findCustomerDocumentsAsZip(applicationId);
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "application/octet-stream");
headers.add("Content-Disposition", "attachment; filename=\"zipFile.zip\"");
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
According to HttpServletResponse doc: calling flush() commits the response. I think you need to call response.getOutputStream().flush(); if you want to use HttpServletResponse. Otherwise, Tim's answer provides an easier way to do it.
You can set the media type as: application/json;charset=UTF-8 by using:
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON_UTF8);

Resources