9

我正在尝试显示在新浏览器窗口中打开的内联 PDF。我有以下情况:

  1. 在一些由 ajax 调用的 ActionListen 中,我生成 PDF 内容,将数据放入会话中,并发送要执行的 Javascript(window.open打开新页面以显示 PDF)
  2. 在打开的页面上,我只有p:media标签里面h:body的值指向StreamedContent

现在,在那个页面上我的 PDF 没有生成。在日志中我可以看到这两行:

org.primefaces.application.PrimeResourceHandler handleResourceRequest
SEVERE: Error in streaming dynamic resource. Expression cannot be null

我开始调试并发现了一些东西。

首先,我在我的bean的@PostConstruct方法中添加了断点。RequestScoped有趣的是,断点到达了两次,在完美显示 PDF 之后,我大吃一惊?!

经过一些调试后,PrimeResourceHandler我发现在某些情况下ValueExpression没有计算,实际上它会抛出NullPointerException,并且在调试时我再次看到发送了两个请求,第二个请求失败,因为dynamicContentId在第一个请求中被删除,第二个调用handleResourceRequest没有有道理。

通过 Firebug,我可以看到两个请求,第一个是 PDF 数据,第二个也是内容类型 application/pdf 但为空,大小为 0。

xhtml页面:

<html>
  <h:head></h:head>
  <h:body>
    <p:media value="#{reportBean.streamedContent}" player="pdf" width="500" height="500"/>
  </h:body>
</html>

后备豆:

@RequestScoped
public class StampaListeBackingBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private StreamedContent streamedContent;

    @PostConstruct
    public void init() {
        Map<String, Object> session = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
        byte[] b = (byte[]) session.get("reportBytes");
        if (b != null) {
            streamedContent = new DefaultStreamedContent(new ByteArrayInputStream(b), "application/pdf");
        }
    }

    public StreamedContent getStreamedContent() {
        if (FacesContext.getCurrentInstance().getRenderResponse()) {
            return new DefaultStreamedContent();
        } else {
            return streamedContent;
        }
}

    public void setStreamedContent(StreamedContent streamedContent) {
        this.streamedContent = streamedContent;
    }
}

我需要了解为什么在带有p:media标签的页面上发送两个请求,并弄清楚如何使其工作。支持 bean 是请求范围的,它StreamedContent@PostConstruct方法中创建,并具有该字段的 getter 和 setter。Primefaces 版本是 3.4.2,Mojarra 是 2.1.14。

添加:

很容易重现我的问题。如果init方法中的代码替换为以下内容:

FileInputStream fis = new FileInputStream(new File("C:\\samplexxx.pdf"));
streamedContent = new DefaultStreamedContent(fis, "application/pdf");

问题可以重现。

4

3 回答 3

10

我可以重现你的问题。它确实在 Firefox 中不起作用(在 IE9 中也不起作用,但它在 Chrome 中起作用)。PrimeFaces 的负责人 Cagatay 也曾多次提到这一点。

我不确定这是否是 PrimeFaces 资源处理程序或浏览器中的错误。我会把它放在中间。

同时,你最好的选择是一个简单的web servlet来完成这项工作。只需创建此类:

@WebServlet("/report.pdf")
public class PdfReportServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        byte[] content = (byte[]) request.getSession().getAttribute("reportBytes");
        response.setContentType("application/pdf");
        response.setContentLength(content.length);
        response.getOutputStream().write(content);
    }

}

并按如下方式调用它:

<p:media value="/report.pdf" ... />

而已。无需 XML 配置。它适用于所有浏览器。根据功能要求,您可能需要进一步微调与浏览器缓存相关的响应标头。

于 2013-01-23T20:52:28.533 回答
6

这不是浏览器或 primefaces 问题,只是一个有趣的 getter 问题。

getter 被 p:media 调用两次(或者如果您刷新页面多次),但只有第一次调用获得正确的数据。StreamedContent 封装了一个 InputStream,它的属性是如果流位于文件末尾,它将不提供任何字节。第一次读到它的结尾(数据没问题),但每次下一次调用都不会得到数据。:)

inputStream.read() 的 javadoc如果由于流在文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储到 b 中。

解决方案

            private StreamedContent streamedContent;
            private InputStream stream;


            public void somewhere(){
                byte[] b = ...
                stream = new ByteArrayInputStream( b );
                stream.mark(0); //remember to this position!
                streamedContent = new DefaultStreamedContent(stream, "application/pdf");
            }


            public StreamedContent getStreamedContent() {
                if (streamedContent != null)
                    streamedContent.getStream().reset(); //reset stream to the start position!
                return streamedContent;
            }
于 2014-06-17T06:13:50.277 回答
3

我希望我的一点贡献可以帮助任何无法在 Firefox 中显示 pdf 预览的人。我使用的是 Primefaces 6 + Spring,我遇到了同样的问题,但可能不是出于同样的原因。事实上,我尝试了 Balus C 提出的解决方案。它帮助我在 Chrome 和 IE11 中显示 pdf,但它仍然无法在 Firefox 52 中运行。

我注意到 Firefox 控制台中出现错误:X-Frame-Options 拒绝加载:http://localhost:8080/myapp/不允许框架

就我而言,这是因为 spring-security 配置和解决方案是以这种方式编辑 spring-context.xml :

<sec:http ...>
...
<sec:headers>        		
         <sec:frame-options policy="SAMEORIGIN" />
</sec:headers>
...
</sec:http>

于 2017-04-02T00:04:14.053 回答