Issue in downloading zip file in Spring REST Service - spring

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

Related

SpringBoot REST produces pdf

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

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 !

How to convert collection to csv with jackson in java spring?

I have a problem to convert a java.util.Collection to a csv file with jackson.
In the following code you can see a method to convert the collection to a csv-string.
But i need a method to convert the collection with com.fasterxml.jackson.
The Enum "DownloadType" get the column and headerlines for csv file.
Do you have an idea to fix them?
#RequestMapping(value = "/csv",
produces = {"text/csv"},
consumes = {"application/json"},
method = RequestMethod.POST)
public ResponseEntity<Object> exportCsv()
{
ResponseEntity<Object> response = null;
try
{
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "text/csv; charset=UTF-8");
headers.add(HttpHeaders.CACHE_CONTROL, "no-store, must-revalidate");
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"export.csv\"");
headers.add(HttpHeaders.EXPIRES, "0");
byte[] csvBytes = null;
byte[] headerBytes = null;
byte[] lineBytes = null;
CsvMapper mapper = new
Collection<User> users = getUsers()
headerBytes = DownloadType.USER.getHeaderLine().getBytes("UTF-8");
lineBytes = mapper.writer(DownloadType.USER.getCsvSchema()).writeValueAsBytes(users);
if (headerBytes != null && lineBytes != null)
{
csvBytes = new byte[headerBytes.length + lineBytes.length];
System.arraycopy(headerBytes, 0, csvBytes, 0, headerBytes.length);
System.arraycopy(lineBytes, 0, csvBytes, headerBytes.length, lineBytes.length);
}
response = new ResponseEntity<>(csvBytes, headers, HttpStatus.OK);
}
catch (Exception e)
{
LOG.error(e.getMessage(), e);
}
return response;
}
Maybe try something like this. By writing the data directly to the servlet response the string will get returned directly back to the client as is without formatting or post-processing.
#RequestMapping(value = "/csv",
produces = {"text/csv"},
consumes = {"application/json"},
method = RequestMethod.POST)
public void exportCsv(HttpServletResponse response)
{
...
String headerString = DownloadType.USER.getHeaderLine()
String data = mapper.writer(DownloadType.USER.getCsvSchema()).writeValueAsString(users);
response.setContentType("text/plain; charset=utf-8");
response.getWriter().print(headerString);
response.getWriter().print(data);
Adapted from:
How to Return CSV Data in Browser From Spring Controller

Calling a different host from Spring Controller

My localhost is : http://localhost:8585/api/getproducts where i use #Requestmapping(/api/getproducts) in my ProductController to get to my product page.
On click of a button, i need to call an api on a different host :
http://10.120.130.22:9292/ and i tried to use the below code in a new Controller to call the host:
#RequestMapping("Trainer/reStaff/")
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody response(#RequestParam("trainingId") final int trainingId, HttpServletRequest request)
throws ClientProtocolException, IOException {
String hostname="http://10.120.130.22:9292/";
CloseableHttpClient httpclient = HttpClients.custom().build();
CloseableHttpResponse response=null;
try{
String uri=hostname+"Trainer/reStaff/?trainingId="+trainingId;
HttpPost httpPost = new HttpPost(uri);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-Type", "application/json");
response = httpclient.execute(httpPost);
String responseData = EntityUtils.toString(response.getEntity());
if(response.getStatusLine().getStatusCode()==200)
System.out.println(responseData+"\n");
else
System.out.println("Error :" + responseData+"\n");
}finally {
httpclient.close();
response.close();
}
But i get the error : HTTP Status 404 - type Status reportmessage description The requested resource is not available.
How do i call the new host from my controller?
I understood how this works. We need to pass the url through httpPost in the service layer :
HttpPost httpPost = new HttpPost(hostUri);
JsonObject jsonResponse = null;
try {
String httpRequestBody = jsonRequestBuilder.build().toString();
logger.info("Request Body: " + httpRequestBody);
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connManager).build();
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(new StringEntity(httpRequestBody));
HttpResponse httpResponse = httpClient.execute(httpPost);
logger.debug("Response Status: " + httpResponse.getStatusLine().getStatusCode());
BufferedReader reader = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
String line;
StringBuffer httpResponseBody = new StringBuffer();
while ((line = reader.readLine()) != null) {
httpResponseBody.append(line);
}
logger.info("Response Body: " + httpResponseBody.toString());
JsonReader jsonReader = Json.createReader(new StringReader(httpResponseBody.toString()));
jsonResponse = jsonReader.readObject();
jsonReader.close();
} catch (Exception ex) {
logger.error("Error occurred while invoking POST on ep: " + hostUrl, ex);
} finally {
httpPost.releaseConnection();
}
logger.debug("Exiting");
return jsonResponse;

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

Resources