将 PDF 文件从 Java Bean 返回到 JSP

发布于 2024-07-06 19:36:54 字数 2824 浏览 8 评论 0原文

编辑:请参阅


简而言之:我有一个 JSP 文件,它调用 Java Bean 中的方法。 该方法创建一个 PDF 文件,理论上将其返回给 JSP,以便用户可以下载它。 但是,在加载 PDF 时,Adobe Reader 会给出错误:文件不以“%PDF-”开头

详细信息: 到目前为止,JSP 成功调用了该方法,创建了 PDF,然后 JSP 出现给用户完成的 PDF 文件。 但是,一旦 Adob​​e Reader 尝试打开 PDF 文件,就会出现错误:文件不以“%PDF-”开头。 为了更好地衡量,我有方法在我的桌面上创建 PDF,以便我可以检查它; 当我在 Windows 中正常打开它时,它看起来很好。 那么为什么 JSP 的输出不同呢?

为了创建 PDF,我使用 Apache FOP。 我正在遵循他们最基本的示例之一,但将生成的 PDF 传递到 JSP 而不是简单地将其保存到本地计算机。 我一直在遵循他们的基本使用模式此示例代码

这是我的 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.printreports.PrintReportsBean" />
<jsp:setProperty name="printReportsBean" property="*"/>

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

这是我的 Java Bean 方法:

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

我意识到这是一个非常具体的问题,但任何帮助将不胜感激!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吾性傲以野 2024-07-13 19:36:54

我过去实现此类功能的方法是让 servlet 将 PDF 文件的内容作为流写入响应。 我不再有源代码了(自从我做任何 servlet/jsp 工作以来已经至少有一年了),但是这里是您可能想要尝试的:

在 servlet 中,获取响应的句柄输出流。 将响应的 mime 类型更改为“application/pdf”,并让 servlet 执行示例中的文件处理。 只是让 servlet 将文件写入输出流,而不是返回 File 对象。 查看文件 i/o 的示例,只需将任何 outfile.write(...) 行替换为 responseStream.write(...) ,您就可以开始了。 一旦你刷新并关闭输出流,并执行返回,如果我没记错的话,浏览器应该能够从响应中获取pdf。

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.

忆梦 2024-07-13 19:36:54

好的,我成功了。 我是这样做的:

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 方法:

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

感谢大家的投入!

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!

深海少女心 2024-07-13 19:36:54

只是猜测,但是您检查过 JSP 页面返回的 MIME 类型吗?

编辑:如果我真的阅读了您发布的代码,我会看到您确实设置了它,所以没关系:)

edit2:您的 JSP 代码中的 JSP 标记之间的换行符不是会出现在输出流中吗? 这会导致服务器返回的响应丢失吗? 我对 PDF 的格式一无所知,但它是否取决于文件中某些位置的某些“标记”字符? (返回的错误消息听起来就像这样)。

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

水溶 2024-07-13 19:36:54

我同意 matt b,也许是 JSP 标签之间的空格。 尝试放置指令

<%@ page trimDirectiveWhitespaces="true" %>

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

<%@ page trimDirectiveWhitespaces="true" %>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文