jsf 2 - 无法在 Primefaces 中的流内容生成的 p:media 中显示 PDF



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

  1. 在ajax调用的一些ActionListen中,我生成PDF内容,将数据放入会话中,并发送Javascript以执行(window.open打开新页面以显示PDF)
  2. 在打开的页面上,我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数据,第二个请求也适用于内容类型应用程序/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 是请求范围的,它@PostConstruct方法中创建StreamedContent,并且具有该字段的 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");

问题可以重现。

我可以重现你的问题。它确实不适用于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 配置。它适用于所有浏览器。根据功能要求,您可能需要进一步微调与浏览器缓存相关的响应标头。

这不是浏览器或素数问题,只是一个有趣的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;
            }

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

我注意到 Firefox 控制台中有一个错误:X 帧选项拒绝加载:http://localhost:8080/myapp/不允许成帧

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

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

相关内容

  • 没有找到相关文章

最新更新