使用JSF 2/CDI提供文件,使用可添加书签的URL



我的主要问题是:是否有一种"好的做法"可以使用带有CDI的JSF 2和可书签的URL来提供二进制文件(PDF、文档等)?

我已经阅读了JSF 2规范(JSR314),我发现它有一段"资源处理"。但它似乎只用于服务war或jar文件中的静态文件。我真的不明白是否存在通过注册一些特定的ResourceHandler进行交互的方式。。。

实际上,我已经习惯了Seam的2种方法:用getResource(HttpServletRequest, HttpServletResponse)方法和getResourcePath()扩展AbstractResource类,以声明在<webapp>/seam/resource/URL前缀之后要服务的路径,并在web.xml文件中声明SeamResourceServlet

以下是我所做的。

我第一次看到如何使用JSF 2.0下载存储在数据库中的文件,并尝试实现它

<f:view ...
    <f:metadata>
        <f:viewParam name="key" value="#{containerAction.key}"/>
        <f:event listener="#{containerAction.preRenderView}" type="preRenderComponent" />
    </f:metadata>
    ...
    <rich:dataGrid columns="1" value="#{containerAction.container.files}" var="file">
        <rich:panel>
                <h:panelGrid columns="2">
                    <h:outputText value="File Name:" />
                    <h:outputText value="#{file.name}" />
                </h:panelGrid>
                <h:form>
                    <h:commandButton value="Download" action="#{containerAction.download(file.key)}" />
                </h:form>
        </rich:panel>
    </rich:dataGrid>

这是豆子:

@Named
@SessionScoped
public class ContainerAction {
    private Container container;
    /// Injections
    @Inject @DefaultServiceInstance
    private Instance<ContainerService> containerService;
    /// Control methods
    public void preRenderView(final ComponentSystemEvent event) {
        container = containerService.get().loadFromKey(key);
    }
    /// Action methods
    public void download(final String key) throws IOException {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
        final ContainerFile containerFile = containerService.get().loadFromKey(key);
        final InputStream containerFileStream = containerService.get().read(containerFile);
        response.setHeader("Content-Disposition", "attachment;filename=""+containerFile.getName()+""");
        response.setContentType(containerFile.getContentType());
        response.setContentLength((int) containerFile.getSize());
        IOUtils.copy(containerFileStream, response.getOutputStream());
        response.flushBuffer();
        facesContext.responseComplete();
    }
    /// Getters / setters
    public Container getContainer() {
        return container;
    }
}

在这里,为了正确解释EL表达式,我不得不切换到Tomcat7(我使用的是6)。有了@SessionScoped,它就起作用了,但@RequestScoped就不起作用了(当我点击按钮时,什么也没发生)。

但后来我想用一个链接而不是按钮。

我尝试了<h:commandLink value="Download" action="#{containerAction.download(file.key)}" />,但它生成了一些丑陋的javascript链接(不可作为书签)。

阅读JSF 2规范,似乎有一个"可书签性"特性,但实际上还不清楚如何使用它

实际上,它似乎只适用于视图,所以我尝试创建一个空视图,并创建了一个h:link:

<h:link outcome="download.xhtml" value="Download">
    <f:param name="key" value="#{file.key}"/>
</h:link>
<f:view ...>
    <f:metadata>
        <f:viewParam name="key" value="#{containerFileDownloadAction.key}"/>
        <f:event listener="#{containerFileDownloadAction.download}" type="preRenderComponent" />
    </f:metadata>
</f:view>
@Named
@RequestScoped
public class ContainerFileDownloadAction {
    private String key;
    @Inject @DefaultServiceInstance
    private Instance<ContainerService> containerService;
    public void download() throws IOException {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        // same code as previously
        ...
        facesContext.responseComplete();
    }

    /// getter / setter for key
    ...
}

但后来,我得了java.lang.IllegalStateException: "getWriter()" has already been called for this response

逻辑上,当视图启动时,它使用getWritter来初始化响应。

因此,我创建了一个Servlet来完成这项工作,并创建了以下h:outputLink:

<h:outputLink value="#{facesContext.externalContext.request.contextPath}/download/">
    <h:outputText value="Download"/>
    <f:param name="key" value="#{file.key}"/>
</h:outputLink>

但是,即使最后一种技术为我的文件提供了一个可添加书签的URL,它也不是真正的"JFS2"。。。

你有什么建议吗?

我同意BalusC的观点。通常,应用程序不仅仅是JSF应用程序,而是JavaEE应用程序。

在Java EE中,除了JSF视图之外,还存在其他用于处理http请求的东西,这并非毫无意义。这是使用Servlet的一种替代方案。在这种情况下,您将使用@Produces和@Path(例如,请参阅使用JERSEY的输入和输出二进制流?)。

另一方面,JSF中<f:viewParam>的一个优点是可以很容易地将验证器附加到它上。Servlet和JAX-RS资源目前都不支持这一点。

CCD_ 14也比一直写CCD_。但是,可以通过将此零件包装在Facelets标记或复合组件中来减轻这种情况。

(我认为,如果规范的未来版本在JSF中提供一个链接标签,直接链接到JAX-RS资源(带有可选的容器启动验证,以确保链接合法),那就太好了)

JSF从一开始就被设计成MVC框架,而不是某种REST文件服务。

servlet非常适合这个工作。使用@WebServlet对其进行注释,以获得更好的Java EE 6感觉。

事实上,使用PrettyFaces URLRevriteFilter可以直接解决这个问题->http://ocpsoft.org/prettyfaces/serving-dynamic-file-content-with-prettyfaces/

这个博客解释了如何做到你想做的事情,而不必使用一个全新的MVC框架。

相关内容

  • 没有找到相关文章

最新更新