Read BLOB (PDF Content) from database and edit and output PDF edited file without creating physical file

29,108

Solution 1

Instead of writing directly to servletOutputStream, you can try this approach:

  1. Create an instance of ByteArrayOutputStream
  2. Create instance of "merged" PDF document. I.e. PDF form Blob + PDF that has title. This example might help : http://java-x.blogspot.com/2006/11/merge-pdf-files-with-itext.html
  3. Write the merged PDF to instance of ByteArrayOutputStream
  4. Set content length of response
  5. Set content type and content disposition
  6. Get bytes from ByteArrayOutputStream and write those bytes to servletOutputStream
  7. Close ByteArrayOutputStream

Solution 2

If you don't want to create temporary files, then you just need to get the PDF as an InputStream from the DB and let iText read from it and finally write to the OutputStream of the response. Your question is actually too broad to give a well-suited answer ("I am not getting anywhere" doesn't give much to work with), so here's a broad answer:

InputStream input = resultSet.getBinaryStream("columnname");
PdfReader reader = new PdfReader(input);
// ...

OutputStream output = response.getOutputStream();
PdfWriter pdfWriter = PdfWriter.getInstance(document, output);
// ...

You can also pass around a ByteArrayOutputStream instead.


Update as per your problems:

  1. You need to set the response headers before you pass the response body to PdfWriter.

    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=output.pdf");
    PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
    // ...
    
  2. Get rid of the following lines. They would only messup things.

    byte[] bytes = blob.getBytes(1, (int) blob.length());
    servletOutputStream.write(bytes, 0, bytes.length);
    servletOutputStream.flush();
    servletOutputStream.close();
    

    The PdfWriter will already write to the response body. You don't need to repeat it yourself.

Share:
29,108
Sangeet Menon
Author by

Sangeet Menon

An Enthusiastic Open Source Programmer SOreadytohelp

Updated on July 17, 2020

Comments

  • Sangeet Menon
    Sangeet Menon almost 4 years

    I am using an Oracle Database and storing PDF content in a BLOB field.

    I want to read the BLOB content and then edit and output the edited content.

    The editing I need to do are:

    • add a title above the BLOB content
    • add a Water mark on every page
    • add footer on every page

    Then I need to output the file without any physical file getting created that is within the response stream.

    I tried to achieve this using itext but was not reaching anywhere with it. I am stuck and not sure where to start with.

    Also sometimes I might have to combine blob contents into one, buts thats some thing that is bound to happen Once in a million..so that not a concern now...

    How can I achieve my primary requirements of the above three steps using in java? Is it possible with Itext?? Or is some other library available that would help?

    Database : Oracle 10g Release 2

    OS: Linux Fedora/Redhat

    Front-end: Java/Servlet/JSP

    EDIT

    Here is what I tried to do

    oracle.sql.BLOB blob = (BLOB) rs.getBlob("MYPDF");
    byte[] bytes = blob.getBytes(1, (int) blob.length());
    InputStream is = blob.getBinaryStream();
    Document document=new Document();
    ServletOutputStream servletOutputStream = response.getOutputStream();
    PdfWriter writer=PdfWriter.getInstance(document, servletOutputStream);
    document.open();
    document.add(new Paragraph("Some title"));
    document.add(new Paragraph("Some title"));
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=output.pdf");
    servletOutputStream.write(bytes, 0, bytes.length);
    servletOutputStream.flush();
    servletOutputStream.close();
    document.close();
    

    The program outputs the pdf content in BLOB field in the database and without the title.

    and when I change a bit in the code (change the order of the last few lines) to:

    document.close();
    servletOutputStream.flush();
    servletOutputStream.close();
    

    I get the document with the title content in it and no pdf content of BLOB field. Its the first thing(servletoutputstream/document) that is closed is been thrown as the output.

    And when I closed the document before putting the blob content in outputstream:

    document.close();
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=output.pdf");
    servletOutputStream.write(bytes, 0, bytes.length);
    servletOutputStream.flush();
    servletOutputStream.close();
    

    I got the browser displaying something like this:

    %PDF-1.4 %���� 2 0 obj <>stream x�+�r �26S�00SI�2P�5��1���BҸ4��sSJ2KrR5C��*P�B�5�+��k)&� endstream endobj 4 0 obj <<<>>>/MediaBox[0 0 595 842]>> endobj 1 0 obj <> endobj 3 0 obj <> endobj 5 0 obj <> endobj 6 0 obj <> endobj xref 0 7 0000000000 65535 f 0000000304 00000 n 0000000015 00000 n 0000000392 00000 n 0000000147 00000 n 0000000443 00000 n 0000000488 00000 n trailer <]/Info 6 0 R/Size 7>> startxref 620 %%EOF 
    

    I need the file to be outputted with the pdf content and the title as well.

    Hope this edit helps a little bit...

    UPDATE(File thrown out in response with the title and the BLOB Content) :

    Document document = new Document(PageSize.A4, 108, 72, 30, 72);
    PdfWriter writer = PdfWriter.getInstance(document, outputstream);
    
    document.open();
    
    ///-----Added Some Title----///
    
    rs = stmt.executeQuery(queryToGetBLOBCONTENT);
    
    if (rs.next()) {
    
    
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=watermark.pdf");
    oracle.sql.BLOB blob = (BLOB) rs.getBlob("MYPDF");
    byte[] bytes = blob.getBytes(1, (int) blob.length());
    InputStream is = blob.getBinaryStream();
    PdfReader pdfReader = new PdfReader(is, bytes);
    BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
    PdfContentByte cb = writer.getDirectContent(); // Holds the PDF
    PdfImportedPage page;
    int currentPageNumber = 0;
    int pageOfCurrentReaderPDF = 0;
    while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
        if (pageOfCurrentReaderPDF > 0) {
            document.newPage();
        }
        pageOfCurrentReaderPDF++;
        currentPageNumber++;
        page = writer.getImportedPage(pdfReader, pageOfCurrentReaderPDF);
        cb.addTemplate(page, 0, 0);
    }
    pageOfCurrentReaderPDF = 0;
    outputstream.flush();
    document.close();
    outputstream.close();
    
    }
    

    This gives me a file in response that has a the BLOB from DB with title on the top and that is done without any physical files getting generated.

    Now to generate the water mark and I need to pass the document to the PDfreader how can I achieve that before closing the document (i.e. executing document.close() , which would out put the file w/o water mark as the stream got closed)

    What am I doing wrong in this code? How can I achieve the same file with the watermark and that too without a file getting created at the background.