PrimeFaces p:media не работает с StreamedContent в bean-компоненте @ViewScoped

У меня проблема, связанная с рендерингом pdf-файла типа meida Primefaces4 в браузере. Я успешно попробовал пример в витрине с сайта Primefaces. Теперь я хочу получить новую функцию, которая предлагает древовидную структуру с узлами документа на левой панели. Пользователь может выбрать один документ, чтобы отобразить его на центральной панели. Это означает, что он генерирует медиа-поле pdf в backbean, когда пользователь выбирает один документ в дереве.

связанный код показан ниже:

задняя часть:

@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:

Я получил это исключение, когда мое приложение запускается:

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 отображался, если пользователь не выбрал файл.


person Cong Wang    schedule 25.09.2013    source источник


Ответы (1)


Ваша конкретная проблема вызвана тем, что веб-браузер фактически загружает файл PDF в физически совершенно отдельном HTTP-запросе, чем HTTP-запрос, который генерирует и отправляет вывод HTML на основе исходного кода JSF. Вы, наверное, уже знаете, что bean-компоненты с областью видимости привязаны к конкретному представлению JSF через javax.faces.ViewState скрытое поле ввода. Если это изменяется или отсутствует, то запрос получает новый и другой экземпляр bean-компонента с областью видимости.

Другими словами, пока браузер загружает PDF-файл с сервера в отдельном HTTP-запросе, он не использует тот же экземпляр @ViewScoped bean-компонента, а вместо этого получает совершенно новый и полностью независимый экземпляр, который не содержит тех же свойств ( состояние) как тот, который привязан к странице, и, таким образом, весь StreamedContent в этой точке просто null.

Эта проблема с <p:media> и StreamedContent по существу имеет те же основания, что и проблема с <p:graphicImage> и StreamedContent, на которую уже несколько раз давался ответ:

В вашем конкретном случае вам необходимо перепроектировать всю связку таким образом, чтобы компонент поддержки DocumentsBean сохранял PDF-файл в каком-то месте (например, на временном диске, в памяти сервера, базе данных и т. д.) с помощью уникального идентификатора, а затем передавать именно этот уникальный идентификатор вместе с параметром запроса на <p:media> следующим образом:

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

При этом вспомогательный компонент MediaManager выглядит примерно так:

@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()));
        }
    }

}
person BalusC    schedule 25.09.2013
comment
Отлично, работает! Ваш блог действительно помогает. На самом деле я только начинаю использовать JSF2, поэтому у меня нет слишком большого опыта в определении категории этой проблемы. Я все еще впитываю. - person Cong Wang; 25.09.2013