首页 文章

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

提问于
浏览
8

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

  • 在一些由ajax调用的ActionListen中,我生成PDF内容,将数据放入会话中,并发送Javascript来执行( window.open 打开新页面以显示PDF)

  • 在打开的页面上,我只在 h:body 内有 p:media 标签,其值指向 StreamedContent

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

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

我开始调试并找出一些东西 .

首先,我将断点添加到 RequestScoped bean的 @PostConstruct 方法中 . 有趣的是,断点达到了两次,并且在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>

支持 beans :

@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是请求作用域,它在 @PostConstruct 方法中创建 StreamedContent ,并为该字段设置getter和setter . Primefaces版本为3.4.2,Mojarra 2.1.14 .

ADDED:

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

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

问题可以再现 .

3 回答

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

  • 6

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

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

    javadoc of inputStream.read():如果没有字节可用,因为流位于文件的末尾,则返回值-1;否则,至少读取一个字节并存储到b中 .

    Solution

    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;
                }
    
  • 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>
    

相关问题