【问题标题】:PrimeFaces p:media not working with StreamedContent in a @ViewScoped beanPrimeFaces p:media 不适用于 @ViewScoped bean 中的 StreamedContent
【发布时间】:2013-09-30 10:01:17
【问题描述】:

我有一个与 Primefaces4 meida 类型在浏览器中渲染 pdf 文件有关的问题。我已经成功地尝试了 primefaces 网站上展示的示例。现在我想获得一个新功能,它在左侧面板中提供带有文档节点的树结构。用户可以选择一个文档以将其显示在中心面板中。这意味着一旦用户在树上的一个文档上进行选择,它就会在 backbean 中生成 pdf 媒体字段。

相关代码如下:

后豆:

@ManagedBean
@ViewScoped
public class DocumentsBean implements Serializable {

private static final long serialVersionUID = 3560539268513760978L;
private TreeNode root;
private String url;
private TreeNode selectedNode; 
private StreamedContent media;

public DocumentsBean() {
    root = new DefaultTreeNode("Root");
}

public TreeNode getRoot() {
    return root;
}

public TreeNode getSelectedNode() {  
    return selectedNode;  
}  

public void setSelectedNode(TreeNode selectedNode) {  
    this.selectedNode = selectedNode;  
}  

public void onNodeSelect(NodeSelectEvent event) {  
    File file = (File) this.selectedNode.getData();
    generatePDF(file);
}

public String getUrl() {
    return url;
}

public void setUrl(String url) {
    this.url = url;
}

public void explore() {
    root = new DefaultTreeNode(new File(this.url), null);
    constructDir(root);
}

/**
 * construct directory and its sub files.
 * @param parent
 */
private void constructDir(TreeNode parent) {
    File file = (File) parent.getData();
    File[] files = file.listFiles();
    for (File f: files) {
        if (f.isFile()) {
            new DefaultTreeNode("document", f, parent);
        } else {
            TreeNode subParent = new DefaultTreeNode(f, parent);
            constructDir(subParent);
        }
    }

}

private void generatePDF(File file) {
    PDFGenerator generator = new PDFGenerator(file);
    File pdf = generator.transformToPDF();

    if (pdf != null) {
        InputStream stream = null;
        try {
            stream = new FileInputStream(pdf);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        media = new DefaultStreamedContent(stream, "application/pdf");
    }

}

public StreamedContent getMedia() {
    return media;
}

}

我的部分观点:

<p:layoutUnit position="west" size="300" header="Directory Content" resizable="false" collapsible="true">
            <h:form id="docTree_form">
                <p:growl id="messages" showDetail="true" /> 
                <p:tree id="docTree" value="#{documentsBean.root}" var="node" animate="true" selectionMode="single" selection="#{documentsBean.selectedNode}" dynamic="true" cache="true">

                    <p:ajax event="select" update=":pdf_form:media" listener="#{documentsBean.onNodeSelect}" />
                    <p:treeNode expandedIcon="ui-icon-folder-open" collapsedIcon="ui-icon-folder-collapsed">
                        <h:outputText value="#{node.name}" />
                    </p:treeNode>

                    <p:treeNode type="document" icon="ui-icon-document">
                        <h:outputText value="#{node.name}" />
                    </p:treeNode>
                </p:tree>
            </h:form>
        </p:layoutUnit>

        <p:layoutUnit position="center" header="Center" resizable="true">
            <h:form id="pdf_form">
                <p:media id="media" value="#{documentsBean.media}"     player="pdf" width="100%" height="700px">  
                    Your browser can't display pdf 
                </p:media>
            </h:form>
        </p:layoutUnit>

当我运行这段代码时,没有错误或异常。但是,Firefox 中没有生成 PDF 查看器。真奇怪!

基于 BalusC cmets 的后续问题:

当我的应用程序运行时出现此异常:

SEVERE: Servlet.service() for servlet [Faces Servlet] in context with path     [/DocumentViewer_JSF] threw exception
java.lang.NullPointerException
at     org.primefaces.application.PrimeResourceHandler.handleResourceRequest(PrimeResourceHandler.java:114)

我发现这条线导致了这个异常:

return new DefaultStreamedContent();

如果我创建一个真正的 pdf 文件,异常就消失了。但是如果用户没有选择文件,我真的不希望显示 pdf 文件。

【问题讨论】:

    标签: pdf jsf primefaces media view-scope


    【解决方案1】:

    您的具体问题是因为网络浏览器实际上是在物理上完全独立的 HTTP 请求中下载 PDF 文件,而不是基于 JSF 源代码生成和发送 HTML 输出的 HTTP 请求。您可能已经知道视图范围的 bean 通过 javax.faces.ViewState 隐藏输入字段绑定到特定的 JSF 视图。如果它被更改或不存在,则请求将获得一个新的不同视图范围的 bean 实例。

    换句话说,当浏览器在单独的 HTTP 请求中从服务器下载 PDF 文件时,它并没有使用相同的 @ViewScoped bean 实例,而是获得了一个全新且完全独立的实例拥有与绑定到页面的属性(状态)相同的属性(状态),因此整个 StreamedContent 在那时只是 null

    &lt;p:media&gt;StreamedContent 的这个问题与&lt;p:graphicImage&gt;StreamedContent 的问题基本相同,之前已经回答了好几次:

    在您的特定情况下,您需要重新设计整个集合,使DocumentsBean 支持 bean 通过唯一标识符将 PDF 文件存储在某个位置(例如临时磁盘、服务器内存、数据库等),然后将该唯一标识符作为请求参数准确传递给&lt;p:media&gt;,如下所示:

    <p:media value="#{mediaManager.stream}" width="100%" height="700px" player="pdf">
        <f:param name="id" value="#{documentsBean.mediaId}" />
    </p:media>
    

    MediaManager 支持 bean 看起来像这样:

    @ManagedBean
    @ApplicationScoped
    public class MediaManager {
    
        @EJB
        private MediaService service;
    
        public StreamedContent getStream() throws IOException {
            FacesContext context = FacesContext.getCurrentInstance();
    
            if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
                // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
                return new DefaultStreamedContent();
            } else {
                // So, browser is requesting the media. Return a real StreamedContent with the media bytes.
                String id = context.getExternalContext().getRequestParameterMap().get("id");
                Media media = service.find(Long.valueOf(id));
                return new DefaultStreamedContent(new ByteArrayInputStream(media.getBytes()));
            }
        }
    
    }
    

    【讨论】:

    • 太好了,工作!你的博客真的很有帮助。我实际上是刚开始使用JSF2,所以我在识别这个问题的类别方面没有太多经验。我还在吸收。
    猜你喜欢
    • 2018-06-15
    • 2012-08-27
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 2012-02-12
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多