Returning a PDF file from a Java Bean to a JSP

EDIT: See my working code in the answers below.


In brief: I have a JSP file which calls a method in a Java Bean. This method creates a PDF file and in theory, returns it to the JSP so that the user can download it. However, upon loading the PDF, Adobe Reader gives the error: File does not begin with '%PDF-'.

In detail: So far, the JSP successfully calls the method, the PDF is created and then the JSP appears to give the user the finished PDF file. However, as soon as Adobe Reader tries to open the PDF file, it gives an error: File does not begin with '%PDF-'. Just for good measure, I have the method create the PDF on my Desktop so that I can check it; when I open it normally within Windows is appears fine. So why is the output from the JSP different?

To create the PDF, I'm using Apache FOP. I'm following one of their most basic examples, with the exception of passing the resulting PDF to a JSP instead of simply saving it to the local machine. I have been following their basic usage pattern and this example code.

Here's my JSP file:

<%@ taglib uri="utilTLD" prefix="util" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ page language="java" session="false" %>
<%@ page contentType="application/pdf" %>

<%-- Construct and initialise the PrintReportsBean --%>
<jsp:useBean id="printReportsBean" scope="request" class="some.package.printreports.PrintReportsBean" />
<jsp:setProperty name="printReportsBean" property="*"/>

<c:set scope="page" var="xml" value="${printReportsBean.download}"/>

Here's my Java Bean method:

//earlier in the class...
private static FopFactory fopFactory = FopFactory.newInstance();

public File getDownload() throws UtilException {

    OutputStream out = null;
    File pdf = new File("C:\\documents and settings\\me\\Desktop\\HelloWorld.pdf");
    File fo  = new File("C:\\somedirectory", "HelloWorld.fo");

    try {

        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

        out = new FileOutputStream(pdf);
        out = new BufferedOutputStream(out);

        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);

        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(); //identity transformer

        Source src = new StreamSource(fo);

        Result res = new SAXResult(fop.getDefaultHandler());

        transformer.transform(src, res);

        return pdf;

    } catch (Exception e) {

         throw new UtilException("Could not get download. Msg = "+e.getMessage());

    } finally {

         try {
             out.close();
         } catch (IOException io) {
             throw new UtilException("Could not close OutputStream. Msg = "+io.getMessage());
         }
    }
}

I realise that this is a very specific problem, but any help would be much appreciated!


Asked by: Daryl341 | Posted: 21-01-2022






Answer 1

The way I have implemented this type of feature in the past is to make a servlet write the contents of the PDF file out to the response as a stream. I don't have the source code with me any longer (and it's been at least a year since I did any servlet/jsp work), but here is what you might want to try:

In a servlet, get a handle on the response's output stream. Change the mime type of the response to "application/pdf", and have the servlet do the file handling you have in your example. Only, instead of returning the File object, have the servlet write the file to the output stream. See examples of file i/o and just replace any outfile.write(...) lines with responseStream.write(...) and you should be set to go. Once you flush and close the output stream, and do the return, if I remember correctly, the browser should be able to pick up the pdf from the response.

Answered by: Miranda533 | Posted: 22-02-2022



Answer 2

Ok, I got this working. Here's how I did it:

JSP:

<%@ taglib uri="utilTLD" prefix="util" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ page language="java" session="false" %>
<%@ page contentType="application/pdf" %>

<%-- Construct and initialise the PrintReportsBean --%>
<jsp:useBean id="printReportsBean" scope="request" class="some.package.PrintReportsBean" />
<jsp:setProperty name="printReportsBean" property="*"/>

<%
    // get report format as input parameter     
    ServletOutputStream servletOutputStream = response.getOutputStream();

    // reset buffer to remove any initial spaces
    response.resetBuffer(); 

    response.setHeader("Content-disposition", "attachment; filename=HelloWorld.pdf");

    // check that user is authorised to download product
    printReportsBean.getDownload(servletOutputStream);
%>

Java Bean method:

//earlier in the class...
private static FopFactory fopFactory = FopFactory.newInstance();

public void getDownload(ServletOutputStream servletOutputStream) throws UtilException {

    OutputStream outputStream = null;

    File fo  = new File("C:\\some\\path", "HelloWorld.fo");

    try {

        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

        outputStream = new BufferedOutputStream(servletOutputStream);

        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outputStream);

        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(); //identity transformer

        Source src = new StreamSource(fo);

        Result res = new SAXResult(fop.getDefaultHandler());

        transformer.transform(src, res);

    } catch (Exception e) {

        throw new UtilException("Could not get download. Msg = "+e.getMessage());

    } finally {

        try {
            outputStream.close();
        } catch (IOException io) {
            throw new UtilException("Could not close OutputStream. Msg = "+io.getMessage());
        }
     }
 }

Thanks to everyone for their input!

Answered by: Catherine313 | Posted: 22-02-2022



Answer 3

Just a guess, but have you checked the MIME type that your JSP page is returning?

edit: if I actually read the code you posted I would see you did set it, so nevermind :)

edit2: Aren't the newlines between JSP tags in your JSP code going to end up in the output stream? Could that throw off the response returned by the server? I don't know anything about the format of a PDF, but does it depend on certain "marker" characters being in certain locations in the file? (The error message returned sounds like it does).

Answered by: Adelaide103 | Posted: 22-02-2022



Answer 4

I agree with matt b, maybe its the spaces between JSP tags. Try putting the directive

<%@ page trimDirectiveWhitespaces="true" %>

Answered by: Aida566 | Posted: 22-02-2022



Similar questions

java - Returning JSON in GWT

I'm still pretty new to JSON and GWT and I'm trying to figure out how to pass JSON data back from a page into my GWT app. I pass the JSON back to a class: public class GetProductTree extends JavaScriptObject { protected GetProductTree() { } public final native String getCustomerName() /*-{ return this.customername; }-*/; } It's pretty basic and not complete at this moment so I...


java - JTable not returning selected row correctly

I am working with an extension of the DefaultTableModel as follows: This is the NEW AchievementTableModel after updating it to reflect input from some answers. public AchievementTableModel(Object[][] c, Object[] co) { super(c,co); } public boolean isCellEditable(int r, int c) {return false;} public void replace(Object[][] c, Object[] co) { setDataVector(convertToVector(c), convertToVector(c...


java - How do you robustly implement a REST service that retrieves DB records then purges them before returning?

Scenario Imagine a REST service that returns a list of things (e.g. notifications) Usage A client will continually poll the REST service. The REST service retrieves records from the database. If records are available, they are converted into JSON and returned to the client. And at the same time, the retrieved records are purged from the DB. Problem How do y...


java - Returning A Value To a Swing Class from another Swing Class

Some background on myself. Former AS/400 guy, recently downsized and unemployed. Taking this opportunity to learn java. I’m fairly new to Java and Netbeans. Since I’m unemployed and not in an organization with ‘experts’, I’m trying to find resources for help. I’m in ATLanta so I’ve joined www.ajug.org in hopes of networking with folks to find resources. I’ve also applied for some 'Obama Bucks' in order t...


java - Returning null from native methods using JNI

I have some native code which returns a jbyteArray (so byte[] on the Java side) and I want to return null. However, I run into problems if I simply return 0 in place of the jbyteArray. Some more information: The main logic is in Java, the native method is used to encode some data into a byte stream. don;t ask.. it has to be done like this. Recently, the native code had to be changed a bit and now it runs horribly h...


Returning char array from java to C - JNI

I have a object store in Java. My C program stores data (in form of char array) in java. Now I wish to retrieve data from my store. I cannot find any function call that returns me an char array. How can I do this?


Java http call returning response code: 501

I am having an issue with this error: **Server returned HTTP response code: 501 for URL: http://dev1:8080/data/xml/01423_01.xml** See this code: private static Map sendRequest(String hostName, String serviceName) throws Exception { Map assets = null; HttpURLConnection connection = null; Authenticator.setDefault(new Authenticator()); ...


Returning C array to Java using JNA

I am not too familiar with C, but I need to use a C library in my java code. I have created the DLL and am able to access it just fine, but I am attempting to return an array of ints from the C code to the java code. In C I thought you could simply return a pointer to the array, but it's not working like I expect in my Java code. Here's the C code: int * getConusXY(double latitude, double longitud...


java - Returning array from method

In my class, I have a method that returns an array like this. double arrValues[] = ClassX.getValues(); I wonder that, does the array size have any influence on performance. Are there any copy operation for arrValues[] or is it only a reference return ?


Returning a C++ class to Java via JNI

I'm currently using both C++ and Java in a project and I'd like to be able to send an object which is contained in C++ to my Java interface in order to modify it via a GUI and then send the modification back in C++. So far I've been returning either nothing, an int or a boolean to Java via the JNI interface. This time I have to send an object through the interface. I have made similar class definition available bot...






Still can't find your answer? Check out these amazing Java communities for help...



Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)



top