我正在尝试显示在新浏览器窗口中打开的内联PDF。我有以下情况:
window.open
打开新页面以显示PDF)在打开的页面上,我在p:media
内只有h:body
标记,其值为StreamedContent
:
现在,在该页面上我的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");
问题可以复制。
答案 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>