我的主要问题是:是否有使用 JSF 2 和 CDI 并使用可收藏的 URL 来提供二进制文件(PDF、文档等)的“良好做法”?
我已经阅读了JSF 2 规范(JSR 314)并且我看到它存在一个“资源处理”段落。但它似乎仅用于提供放入 war 或 jar 文件中的静态文件。我真的不明白它是否存在通过注册一些特定的 ResourceHandler 来进行交互的方式......
实际上,我已经习惯了 Seam 的 2 种方法:使用方法扩展AbstractResource
类并在URL 前缀之后声明要服务的路径并在文件中声明。getResource(HttpServletRequest, HttpServletResponse)
getResourcePath()
<webapp>/seam/resource/
SeamResourceServlet
web.xml
这就是我所做的。
我首先看到了如何使用 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;
}
}
在这里,我必须切换到 Tomcat 7(我使用的是 6)才能正确解释该 EL 表达式。它@SessionScoped
起作用了,但不起作用@RequestScoped
(当我单击按钮时,什么也没发生)。
但后来我想使用链接而不是按钮。
我试过<h:commandLink value="Download" action="#{containerAction.download(file.key)}" />
了,但它会生成一些丑陋的 javascript 链接(不可收藏)。
阅读 JSF 2 规范,似乎有一个“Bookmarkability”功能,但并不清楚如何使用它。
实际上,它似乎只适用于视图,所以我尝试创建一个空视图并创建了一个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,它也不是真正的“JFS 2”......
你有什么建议吗?