无法在p:从Primefaces中的流内容生成的媒体中显示PDF

时间:2013-01-23 16:31:38

标签: pdf jsf-2 primefaces stream media

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

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

  3. 现在,在该页面上我的PDF未生成。在日志中我可以看到这两行:

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

    我开始调试并找出一些事情。

    首先,我将断点添加到@PostConstruct bean的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>
    

    支持bean:

    @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标记的页面上发送了两个请求,并弄清楚如何使其工作。 Backing 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");
    

    问题可以复制。

3 个答案:

答案 0 :(得分:9)

我可以重现你的问题。它确实在Firefox中不起作用(也不适用于IE9,但它适用于Chrome)。 PrimeFaces领导Cagatay也提到了several times

我不确定这是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配置。它适用于所有浏览器。根据功能要求,您可能希望进一步微调与浏览器缓存相关的响应标头。

答案 1 :(得分:6)

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

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

javadoc of inputStream.read()如果没有字节可用,因为流位于文件的末尾,则返回值-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;
            }

答案 2 :(得分: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>