我正在尝试显示在新浏览器窗口中打开的内联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数据,第二个请求也适用于内容类型应用程序/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>