Streaming Merge PDF - java

I´m looking for a Java PDF Merger solution where I can streaming the merged pdf while I getting (example from a REST API) the PDF pages parts from a REST api.
A pseudo code should be something like this:
public void doGet(HttpServletRequest req, HttpServletResponse res) throws Exception {
sOut = res.getOutputStream();
MergeDocument merger = MergeDocument.merge(sOut);
for (int i = 0; i < 1000; i++) {
byte[] contentPDF = restClient.get("http://mywebsite.com/files/mypdf"+i+".pdf");
merger.append(contentPDF);
sOut.flush(); // sending merged PDF bytes now
}
sOut.close();
}
My point is to not wast heap memory with all PDFs in memory before start sending it to user. In other words, when I get a "contentBytes pdf" from rest I want to send it to the user as a streaming now.
Hope someone can help me :)

Using itextpdf
package com.example.demo.controller;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/pdf")
public class PdfMerger {
#GetMapping
public void merge(HttpServletResponse response) {
Document document = new Document(PageSize.LETTER);
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename=\"merged.pdf\"");
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
PdfCopy copy = new PdfCopy(document, outputStream);
document.open();
for (InputStream file : getPdfs()) {
copy.addDocument(new PdfReader(file)); // writes directly to the output stream
}
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document.isOpen()) {
document.close();
}
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
private List<InputStream> getPdfs() {
List<InputStream> list = new ArrayList<>();
for (int i = 0; i < 10; i++){
list.add(PdfMerger.class.getResourceAsStream("/pdf/1.pdf"));
list.add(PdfMerger.class.getResourceAsStream("/pdf/2.pdf"));
}
return list;
}
}

Related

Download File to Downloads Folder From Server ActionForward/Struts/Java

I am trying to download a file from my web application in an ActionForward java class. I have looked at many examples to try different solutions but none have worked so far. My knowledge is limited and have spent a good amount of time to get this to work.
From my jsp page a link hits an action in my struts config which takes the thread to an ActionForward return type method on a java class.
I then take the passed in file name and grab it from an amazon s3 bucket. With the file downloaded from the s3 bucket I now have the file bytes[].
I need to then have the file download to the local machine as most files do (appearing in the downloads folder and the web showing the download at the bottom bar of the page)
After following some examples I kept getting this error
Servlet Exception - getOutputStream() has already been called for this
response
I got past the error by doing
response.getOutputStream().write
Instead of creating a new OutputStream like this
OutputStream out = response.getOutputStream();
Now it runs without errors but no file gets downloaded.
Here is the java file I am attempting to do this in.
As you can see in the file below is a commented out DownloadServlet class which I tried as another attempt. I did this because a lot of the examples have classes the extends HttpServlet which I made DownloadServlet extend but it made no difference.
package com.tc.fms.actions;
import com.sun.media.jai.util.PropertyUtil;
import com.tc.fw.User;
import org.apache.commons.beanutils.PropertyUtils;
import java.io.*;
import java.io.File;
import java.util.ArrayList;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.tc.fw.actions.BaseAction;
import org.apache.struts.upload.FormFile;
import io.isfs.utils.ObjectUtils;
import com.tc.fw.*;
import com.tc.fms.*;
import com.tc.fms.service.*;
public class FileDownloadAction extends BaseAction {
private static ObjectUtils objectUtils = new ObjectUtils();
private final int ARBITARY_SIZE = 1048;
public ActionForward performWork(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("In File Download Action");
ActionMessages errors = new ActionMessages();
User user = (User)request.getSession().getAttribute(User.lookupKey);
String fileName = (String) PropertyUtils.getSimpleProperty(form, "fileName");
String outboundDir = (String) PropertyUtils.getSimpleProperty(form, "outboundDir");
System.out.println("File Dir: " + outboundDir + " File Name: " + fileName);
try{
try {
// Get file from amazon
byte[] fileBytes = objectUtils.getFileDavid(outboundDir, fileName);
if (fileBytes != null) {
java.io.File file = File.createTempFile(fileName.substring(0, fileName.lastIndexOf(".") - 1), fileName.substring(fileName.lastIndexOf(".")));
FileOutputStream fileOuputStream = new FileOutputStream(file);
fileOuputStream.write(fileBytes);
try {
/* DownloadServlet downloadServlet = new DownloadServlet();
downloadServlet.doGet(request, response, file);*/
response.setContentType("text/plain");
response.setHeader("Content-disposition", "attachment; filename=" + file.getName());
InputStream in = new FileInputStream(file);
/*OutputStream out = response.getOutputStream();*/
byte[] buffer = new byte[ARBITARY_SIZE];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
response.getOutputStream().write(buffer, 0, numBytesRead);
}
} catch (Exception e) {
System.out.println("OutputStream EROOR: " + e);
}
} else {
System.out.println("File Bytes Are Null");
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("fms.download.no.file.found"));
saveErrors(request, errors);
return mapping.findForward("failure");
// Failed
}
} catch (Exception eee){
System.out.println("Failed in AWS ERROR: " + eee);
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("fms.download.failed"));
saveErrors(request, errors);
return mapping.findForward("failure");
}
}catch (Exception ee){
System.out.println("Failed in global try");
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("fms.download.failed"));
saveErrors(request, errors);
return mapping.findForward("failure");
}
return mapping.findForward("success");
}
}

Create a new Pdf from an existing Pdf and (HTML + CSS)

My use case is that I am generating pdf on the fly. Also I have a pdf with single page. I want to concatenate the newly generated PDF after/before the existing pdf page.
I was already able to generate PDF from HTML (this may result in 2-3 pages) Pdf from HTML with CSS
I tried looking up at the examples one of which is to concatenate existing PDFs pagewise Working with existing PDFs - Concatenate
This page shows exactly what you request with prepend and append static PDF with composed HTML and CSS content.
http://cloudformatter.com/CSS2Pdf.CustomTipsTricks.InjectPDF
Use instructions are here
http://cloudformatter.com/CSS2Pdf.APIDoc.Usage
Try this example:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
public class UtilPDF {
public static void main(String[] args) {
try {
List<InputStream> pdfs = new ArrayList<InputStream>();
File pdfDir = new File("C:\\PDF");
boolean pdfDirectoryExists = true;
if (!pdfDir.exists()) {
pdfDirectoryExists = pdfDir.mkdir();
}
if (pdfDirectoryExists) {
pdfs.add(new FileInputStream("C:\\PDF\\Document1.pdf"));
pdfs.add(new FileInputStream("C:\\PDF\\Document2.pdf"));
OutputStream output = new FileOutputStream("C:\\Projects\\FinalDocument_1_2.pdf");
UtilPDF.concatPDFs(pdfs, output, true);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void concatPDFs(List<InputStream> streamOfPDFFiles, OutputStream outputStream, boolean paginate) {
Document document = new Document();
try {
List<InputStream> pdfs = streamOfPDFFiles;
List<PdfReader> readers = new ArrayList<PdfReader>();
int totalPages = 0;
Iterator<InputStream> iteratorPDFs = pdfs.iterator();
// Create Readers for the pdfs.
while (iteratorPDFs.hasNext()) {
InputStream pdf = iteratorPDFs.next();
PdfReader pdfReader = new PdfReader(pdf);
readers.add(pdfReader);
totalPages += pdfReader.getNumberOfPages();
}
// Create a writer for the outputstream
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
PdfContentByte cb = writer.getDirectContent(); // Holds the PDF
// data
PdfImportedPage page;
int currentPageNumber = 0;
int pageOfCurrentReaderPDF = 0;
Iterator<PdfReader> iteratorPDFReader = readers.iterator();
// Loop through the PDF files and add to the output.
while (iteratorPDFReader.hasNext()) {
PdfReader pdfReader = iteratorPDFReader.next();
// Create a new page in the target for each source page.
while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
document.newPage();
pageOfCurrentReaderPDF++;
currentPageNumber++;
page = writer.getImportedPage(pdfReader, pageOfCurrentReaderPDF);
cb.addTemplate(page, 0, 0);
// Code for pagination.
if (paginate) {
cb.beginText();
cb.setFontAndSize(bf, 9);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, "" + currentPageNumber + " of " + totalPages,
520, 5, 0);
cb.endText();
}
}
pageOfCurrentReaderPDF = 0;
}
outputStream.flush();
document.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document.isOpen())
document.close();
try {
if (outputStream != null)
outputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}

How to POST a multipart/form data with files programatically in a REST API

I am having a Rest URL with form data which i succesfully execute from my REST Client POST MAN.How to do this from a java program ? How to pass the attached files programtically. The snapshot of the rest call is as follows.
check the below code example.
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataParam;
#Path("/file")
public class UploadFileService {
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
#FormDataParam("file") InputStream uploadedInputStream,
#FormDataParam("file") FormDataContentDisposition fileDetail,
#FormDataParam("path") String path) {
// Path format //10.217.14.97/Installables/uploaded/
System.out.println("path::"+path);
String uploadedFileLocation = path
+ fileDetail.getFileName();
// save it
writeToFile(uploadedInputStream, uploadedFileLocation);
String output = "File uploaded to : " + uploadedFileLocation;
return Response.status(200).entity(output).build();
}
// save uploaded file to new location
private 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();
}
}

Jersey2.13: MessageBodyReader not found for StreamingOutput

I'm returning a StreamingOutput inside a Response object:
#GET
#Path("/downloadFile/{filename}")
#Produces(MediaType.TEXT_PLAIN)
public Response downloadFile(#PathParam("filename") String fileName) {
LOG.debug("called: downloadFile({})", fileName);
final File f = new File("/tmp/" + fileName);
try {
if (f.exists()) {
StreamingOutput so = new StreamingOutput() {
#Override
public void write(OutputStream os) throws IOException,
WebApplicationException {
FileInputStream fis = new FileInputStream(f);
byte[] buffer = new byte[4 * 1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
LOG.debug("streaming file contents #{}", bytesRead);
os.write(buffer, 0, bytesRead);
}
fis.close();
os.flush();
os.close();
}
};
return Response.ok(so, MediaType.TEXT_PLAIN).build();
} else {
return createNegativeXmlResponse("file not found or not readable: '"
+ f.getPath() + "'");
}
} catch (Exception e) {
return handle(e);
}
}
Client side (Junit test case):
#Test
public void testDownloadFile() throws Exception {
Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class).build();
WebTarget target = client.target(BASE_URI).path("/downloadFile/b.txt");
Response r = target.request(MediaType.TEXT_PLAIN_TYPE).get();
System.out.println(r.getStatus());
Object o = r.readEntity(StreamingOutput.class);
StreamingOutput so = (StreamingOutput) o;
}
The server runs in a tomcat7 instance. What I get on the client side when r.readEntity is executed is this:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=text/plain, type=interface javax.ws.rs.core.StreamingOutput, genericType=interface javax.ws.rs.core.StreamingOutput.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:230)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
...
How can I get the StreamingOutput object from the Response object on the client side ?
StreamingOutput is a helper class to allow us to write directly to the response output stream, but is not meant to be recreated from the response, so there is no reader to convert the byte stream to a StreamingOutput. We could simple get an InputStream from the response though.
Response response = target.request().get();
InputStream is = response.readEntity(InputStream.class);
Full example:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class TestStreamingOutput extends JerseyTest {
#Path("/streaming")
public static class StreamingResource {
#GET
public StreamingOutput getImage() throws Exception {
final InputStream is
= new URL("http://i.stack.imgur.com/KSnus.gif").openStream();
return new StreamingOutput() {
#Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
byte[] buffer = new byte[4 * 1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
out.close();
is.close();
}
};
}
}
#Override
protected Application configure() {
return new ResourceConfig(StreamingResource.class);
}
#Test
public void test() throws Exception {
Response response = target("streaming").request().get();
InputStream is = response.readEntity(InputStream.class);
ImageIcon icon = new ImageIcon(ImageIO.read(is));
JOptionPane.showMessageDialog(null, new JLabel(icon));
}
}
Only Maven dependency
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.13</version>
<scope>test</scope>
</dependency>
Result:

build FAST web service to download an image from aws s3 using jersey

i am trying to build web service to download an image from aws s3 using jersey 1.18
i have S3ObjectInputStream with the file.
i need FAST way to retrive the image, my way is very slow (5 seconds)
what is the right way to do that?
here is my code
import java.io.InputStream;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
#Path("/getfile")
public class Temp3 {
#GET
#Produces("image/*")
public Response getFile() throws IOException {
System.out.println("in getfile");
awsBL _bl = new awsBL();
S3Object object = _bl.getFile("gps.png");
//System.out.println("**meta:\n"+object.getObjectMetadata());
InputStream objectContent = object.getObjectContent();
InputStream reader = new BufferedInputStream(objectContent);
File file = new File("localFilename");
OutputStream writer = new BufferedOutputStream(new FileOutputStream(file));
int read = -1;
while ( ( read = reader.read() ) != -1 ) {
writer.write(read);
}
writer.flush();
writer.close();
reader.close();
String filename = object.getKey();
ResponseBuilder response = Response.ok(file);
response.header("Content-Disposition",
"attachment; filename="+filename);
return response.build();
}
}
Step one
static byte[] getBinaryData(String filename, String logId) {
return S3_SDK.download(S3_SDK.getFilesBucket(), "/foldername/" + filename, logId);
}
Step two
public static byte[] download(String bucketName, String name, String logId) {
LOG.log(Level.INFO, "{0} :: start download process, bucketName: {1}, name: {2}", new Object[]{logId, bucketName, name});
S3Object object = downloadAsS3Object(bucketName, name, logId);
LOG.log(Level.INFO, "{0} :: download process returns, S3Object: {1}", new Object[]{logId, object});
try {
return IOUtils.toByteArray(object.getObjectContent());
} catch (IOException ex) {
LOG.log(Level.SEVERE, "{0} :: error download process, bucketName: {1}, name: {2}\n{3}", new Object[]{logId, bucketName, name, Utilities.getStackTrace(ex)});
}
return null;
}

Categories

Resources