Read attached PDF file using spring - java

I have a Get API by which can download PDF. Using Spring rest template I am able to get content but when I am creating PDF file it's creating a blank pdf.
I am using byte[] to create a new file.
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_PDF));
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> result =
restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
String content = response.getBody();
byte[] bytes = content.getBytes();
Files.write(Paths.get("/home/123.pdf"), bytes, StandardOpenOption.CREATE );
Please suggest me anyway to do it, Finally my objective
to upload in S3.

Your request headers should also include MediaType.APPLICATION_OCTET_STREAM
This response object will return a byte array- which will be your pdf.
So, the complete example would be something like this-
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_PDF, MediaType.APPLICATION_OCTET_STREAM));
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<byte[]> result =
restTemplate.exchange(uri, HttpMethod.GET, entity, byte[].class);
byte[] content = result.getBody();
Files.write(Paths.get("/home/123.pdf"), content, StandardOpenOption.CREATE );
Hope this helps.

You can Use PDFbox(or itext) API, which is a great way of parsing and creating PDF. It will allow you to place your text etc,
That said
public static void writeToFIle(InputStream uploadedInputStream, String uploadedFileLocation) {
try {
OutputStream out = new FileOutputStream(new File(uploadedFileLocation));
int read = 0;
byte[] bytes = new byte[1024];
out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
should be enough to create a PDF, if its blank check if the value arn't null
I personnaly use jersey to link client and server so i cant really tell you if your method to get the pdf works

Related

how return multivalued response

I'm currently writing a post API that gets a list of invoices number and then calls another API with resttamplate to obtain a pdf for every invoice number after that I concatenate all these pdf files to one file and return this file as a response, the problem here that there are invoices have an invalid invoice number so when I send this invoice number to the rest API can't get pdf so I want to get the failed invoices and send them back to the caller of my rest API, how to return pdf of the successful invoice and JSON object that contain a list of failed invoices number. Thanks in advance
that's my postApi
#PostMapping(value = "/gen-report")
public ResponseEntity<byte[]> generateReport(
#RequestHeader(value = HttpHeaders.AUTHORIZATION) String headerAuthorization) {
byte[] res = null;
List<String> failedInvoices = new ArrayList<>();
ResponseEntity<byte[]> response = null;
ArrayList<RequestParameters> requests = new ArrayList<>();
RequestParameters rp1 = new RequestParameters("360", "3600382368", "N");
RequestParameters rp2 = new RequestParameters("360", "3600382367", "N");
requests.add(rp1);
requests.add(rp2);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
List<byte[]> responses = new ArrayList<>();
for (RequestParameters parameter : requests) {
MultiValueMap<String, String> map = mobileOrderReportService.genrateReportService(parameter);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
response = null;
byte[] content = null;
content = requestReportByInvoiceNumber(entity);
if (content != null) {
responses.add(content);
} else {
failedInvoices.add(parameter.getOrderNum());
}
}
try {
res = mergePDF(responses);
} catch (DocumentException ex) {
Logger.getLogger(MobileOrderReportController.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MobileOrderReportController.class.getName()).log(Level.SEVERE, null, ex);
}
headers.setContentType(MediaType.parseMediaType("application/pdf"));
String filename = "pdf1.pdf";
headers.add("content-disposition", "inline;filename=" + filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
response = new ResponseEntity<byte[]>(res, headers, HttpStatus.OK);
return response;
}
this method returns the byte[] with the successful invoice or null with the failed invoice
public byte[] requestReportByInvoiceNumber(HttpEntity<MultiValueMap<String, String>> entity) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<byte[]> response = null;
try {
response = restTemplate.exchange(mobileOrderReportService.getUrl(), HttpMethod.POST, entity,
byte[].class);
byte[] content = response.getBody();
return content;
} catch (RestClientException ex) {
logger.error("request to UReport failed in requestReportByInvoiceNumber method !...");
return null;
}
}
method merge pdf and return one pdf
public byte[] mergePDF(List<byte[]> pdfFilesAsByteArray) throws DocumentException, IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Document document = null;
PdfCopy writer = null;
for (byte[] pdfByteArray : pdfFilesAsByteArray) {
try {
PdfReader reader = new PdfReader(pdfByteArray);
int numberOfPages = reader.getNumberOfPages();
if (document == null) {
document = new Document(reader.getPageSizeWithRotation(1));
writer = new PdfCopy(document, outStream); // new
document.open();
}
PdfImportedPage page;
for (int i = 0; i < numberOfPages;) {
++i;
page = writer.getImportedPage(reader, i);
writer.addPage(page);
}
} catch (Exception e) {
e.printStackTrace();
}
}
document.close();
outStream.close();
return outStream.toByteArray();
}
You already tagged "Multipart", which could be a solution. You're currently not sending one but just a byte array i.e. a file. With a multipart response, you could indeed have multiple (or in your case 2) parts:
Part with the merged PDF
List of failed invoice numbers either as plain text, JSON, or however you would like to send it.
A multipart response looks like this https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html (scroll down to the example)
An easier and "dirtier" way would be, to just include the faulty invoice numbers in your header response. You can define custom headers, so feel free to name it as you wish.
Either way, your client needs to be adapted, either by being able to read a multipart response (for which you need to write an HttpMessageConverter if your client isn't communicating reactively (Webflux)) or by reading the custom header.

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

Outputstream start writing when inputstream starts reading

I want to send a big object (lets say 4G) over http.
We have a custom serializer that writes the object to an OutputStream. At the moment we write the object to disk and use that file for the inputstream which is used for the request.
Something like these lines:
private static Response sendObject(Object bigObject) throws IOException {
File tempFile = File.createTempFile("x", "y");
OutputStream out = new FileOutputStream(tempFile);
CustomSerializer.serialize(bigObject, out);
out.close();
WebTarget resource = service.path("data");
FormDataMultiPart multiPartEntity = new FormDataMultiPart();
InputStream inputStream = new FileInputStream(tempFile);
StreamDataBodyPart streamBodyPart = new StreamDataBodyPart(
"data",
inputStream,
"data",
MediaType.APPLICATION_OCTET_STREAM_TYPE);
MultiPart multiPart = multiPartEntity.bodyPart(streamBodyPart);
return resource.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(multiPart, multiPart.getMediaType()));
}
We save some memory because we don't serialize to a byte array in memory. Thats nice. But could I save the memory without writing to disk.
Could you write directly to the input stream without rewriting the CustomSerializer?
Could you write directly to the input stream while it reads into the request?
-
It is a little hard to explain, But I think I am after something like this pseudo code:
private static Response sendObject(Object bigObject) throws IOException {
WebTarget resource = service.path("data");
FormDataMultiPart multiPartEntity = new FormDataMultiPart();
// A type of stream I don't know if exist
OutputStream outIn = new OutputInputStream() {
public void openInputStream() {
CustomSerializer.serialize(bigObject, this);
}
};
StreamDataBodyPart streamBodyPart = new StreamDataBodyPart(
"data",
outIn.getInputStream(),
"data",
MediaType.APPLICATION_OCTET_STREAM_TYPE);
MultiPart multiPart = multiPartEntity.bodyPart(streamBodyPart);
return resource.request(MediaType.APPLICATION_JSON_TYPE)
.post(Entity.entity(multiPart, multiPart.getMediaType()));
}
You could use a StreamingOutput and use your CustomSerializer to write to the provided OutputStream
StreamingOutput entity = new StreamingOutput() {
#Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
CustomSerializer.serialize(bigObject, out);
}
};
The write() method will be called by Jersey, giving you a chance to write directly to response entity stream.
Then just use a FormDataBodyPart
BodyPart bigPart = new FormDataBodyPart(
"data", entity, MediaType.APPLICATION_OCTET_STREAM_TYPE);
MultiPart multiPart = new FormDataMultiPart().bodyPart(bigPart);

send a file from the server side to the client side using rest service

I want to send a file from the server side to the client side using rest service
I'm using spring MVC. I used this service method:
public ResponseEntity<InputStreamResource> postFile() throws Exception {
DocumentDaoImpl dao = new DocumentDaoImpl();
Document docCmis = (Document) dao.getDocument("workspace://SpacesStore/ae6d1722-0f08-49ab-a73b-c07036001318");
byte[] myByteArray = readContent(docCmis.getContentStream().getStream());
ClassPathResource myFile = new ClassPathResource(docCmis.getContentStreamFileName());
//System.out.println("eeeee"+pdfFile);
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity
.ok()
.headers(headers)
.contentLength(myByteArray.length)
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(docCmis.getContentStream().getStream()));
}
and this function in a rest controller class
#RequestMapping(value = "/downloadPDFFile",produces = { "application/json" }, method = RequestMethod.GET)
#ResponseBody
public ResponseEntity downloadPDFFile() throws Exception {
return courriersArrivésServices.postFile();
}
then with a rest call using RestTemplate class,i tryied to get my file
Map<String, Object> selectedCourrier=restTemplate.getForObject(SERVER_URI + "/getCourrierDetails"+"?id="+id, HashMap.class);
But that didn't work for me and gives me this error
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.http.ResponseEntity] and content type [application/octet-stream]
Server side:
#RequestMapping("/download")
public byte[] download() throws Exception {
File f = new File("C:\\WorkSpace\\Text\\myDoc.txt");
byte[] byteArray = new byte[(int) f.length()];
byteArray = FileUtils.readFileToByteArray(f);
return byteArray;
}
Client side:
private ResponseEntity<byte[]> getDownload(){
URI end = URI.create("http://vmvdi05059:8080/ePaymentsWeb/download");
return rest.getForEntity(end,byte[].class);
}
public static void main(String[] args) throws Exception {
byte[] byteArray = new TestClient().getDownload().getBody();
FileOutputStream fos = new
FileOutputStream("C:\\WorkSpace\\testClient\\abc.txt");
fos.write(byteArray);
fos.close();
System.out.println("file written successfully..");
}

Download large file through Spring rest template

Server Code :
#POST
#Path("reportDownload")
#Consumes(MediaType.APPLICATION_JSON)
public Response generateReport(QueryData queryData) {
File file = new File("report.xlsx") // large file
StreamingOutput stream = new FileStreamingOutput(file) ;
return Response.ok(stream, MediaType.APPLICATION_OCTET_STREAM)
.header("filename" , file.getName())
.build();
}
Client Code :
Using the following code I'm able to download files upto some limit. Getting out of memory heap error for large files.
final String uri = buildUri("/reportGenerate/reportDownload");
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(read_timeout);
factory.setConnectTimeout(connection_timeout);
RestTemplate restTemplate = new RestTemplate(factory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
List<MediaType> mediaTypeList = new ArrayList<>();
mediaTypeList.add(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccept(mediaTypeList);
HttpEntity entity = new HttpEntity(queryData, headers);
ResponseEntity<byte[]> data = restTemplate.exchange(uri, HttpMethod.POST, entity, byte[].class);
HttpHeaders responseHeader = data.getHeaders();
String fileName = (String) responseHeader.get("filename").get(0);
String downloadFolder = ApplicationConfig.REPORT_DOWNLOAD_FOLDER.getValue();
if (data.getStatusCode() == HttpStatus.OK) {
FileOutputStream fos = null;
File toFile = null;
try {
toFile = new File(downloadFolder + File.separator + fileName);
fos = new FileOutputStream(toFile);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.write(data.getBody(), bos);
bos.writeTo(fos);
} catch (Exception e) {
convertReportException(e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException ex) {
convertReportException(ex);
}
}
return toFile;
}
}
How to use stream for download larger files.
Here is how I do it with a ResponseExtractor. Based on hints from this Spring Jira issue.
RestTemplate restTemplate // = ...;
// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
// Streams the response instead of loading it all in memory
ResponseExtractor<Void> responseExtractor = response -> {
// Here I write the response to a file but do what you like
Path path = Paths.get("some/path");
Files.copy(response.getBody(), path);
return null;
};
restTemplate.execute(URI.create("www.something.com"), HttpMethod.GET, requestCallback, responseExtractor);
update
Here is what RestTemplate does behind the scenes on postForObject and friends (inline comments from me):
#Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
// From RequestCallback's javadoc:
// Callback interface for code that operates on a ClientHttpRequest.
// Allows to manipulate the request headers, and write to the request body.
//
// Used internally by the RestTemplate, but also useful for application code.
RequestCallback requestCallback = httpEntityCallback(request, responseType);
// HttpMessageConverterExtractor checks the response type header and requested
// responseType class to select the proper message converter to handle the response.
// It also implements ResponseExtractor.
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
/**
* Returns a request callback implementation that writes the given object to the
* request stream.
*/
protected <T> RequestCallback httpEntityCallback(Object requestBody, Type responseType) {
return new HttpEntityRequestCallback(requestBody, responseType);
}
Note: This is essentially a duplicate of my answer at https://stackoverflow.com/a/38664475/1030527 but I can't mark the questions as duplicate since neither this one or that one have upvoted answers.
At the client that you mentioned
don't store the file in memory to download a large via the RestTemplate, it can cause the Java heap exception.
it should be stored on disk.
Here is some code sample to download a large file via the RestTemplate
#GetMapping("largeFile")
public ResponseEntity<InputStreamResource> downloadLargeFile(
#RequestParam("fileName") String fileName
) throws IOException {
RestTemplate restTemplate = new RestTemplate();
// Optional Accept header
RequestCallback requestCallback = request -> request.getHeaders()
.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
// Streams the response instead of loading it all in memory
ResponseExtractor<InputStreamResource> responseExtractor = response -> {
// Here I write the response to a file but do what you like
Path path = Paths.get("tmp/" + fileName);
Files.copy(response.getBody(), path, StandardCopyOption.REPLACE_EXISTING);
return new InputStreamResource(new FileInputStream(String.format("tmp/%s", fileName)));
};
InputStreamResource response = restTemplate.execute(
String.format("http://%s:%s/file/largeFileRestTemplate?fileName=%s", host, "9091", fileName),
HttpMethod.GET,
requestCallback,
responseExtractor
);
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", fileName))
.body(response);
}

Categories

Resources