我用的是Primefaces
p: fileDownload
下载不在类路径中的文件。
所以我传递FileInputStream作为参数DefaultStreamedContent。
当我的bean保持在@SessionScoped…,
时,一切都很好。但
. io .NotSerializableException: java.io.FileInputStream
当我将bean保存在@Viewscoped中时抛出。
我代码:DownloadBean.java
@ManagedBean
@ViewScoped
public class DownloadBean implements Serializable {
private StreamedContent dFile;
public StreamedContent getdFile() {
return dFile;
}
public void setdFile(StreamedContent dFile) {
this.dFile = dFile;
}
/**
* This Method will be called when download link is clicked
*/
public void downloadAction()
{
File tempFile = new File("C:/temp.txt");
try {
dFile = new DefaultStreamedContent(new FileInputStream(tempFile), new MimetypesFileTypeMap().getContentType(tempFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
index.xhtml
<h:form>
<h:commandLink action="#{downloadBean.downloadAction}">
Download
<p:fileDownload value="#{downloadBean.dFile}"/>
</h:commandLink>
</h:form>
没有办法使它工作吗?
抛出NotSerializableException
是因为视图范围由JSF视图状态表示,在服务器端状态保存的情况下,JSF视图状态可以序列化为HTTP会话,在客户端状态保存的情况下,可以序列化为HTML隐藏输入字段。FileInputStream
不能用串行形式表示。
如果您绝对需要保持bean视图的作用域,那么您不应该将StreamedContent
声明为实例变量,而是在getter方法中重新创建它。的确,在getter方法中执行业务逻辑通常是不受欢迎的,但是StreamedContent
是一个相当特殊的情况。在动作方法中,您应该只准备可序列化的变量,这些变量稍后将在DefaultStreamedContent
构造期间使用。
@ManagedBean
@ViewScoped
public class DownloadBean implements Serializable {
private String path;
private String contentType;
public void downloadAction() {
path = "C:/temp.txt";
contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(path);
}
public StreamedContent getdFile() throws IOException {
return new DefaultStreamedContent(new FileInputStream(path), contentType);
}
}
(注意,我还修正了您获取内容类型的方式;通过web.xml
<mime-mapping>
条目,您可以更自由地配置mime类型。顺便说一下,<p:graphicImage>
与StreamedContent
有完全相同的问题。另见使用p:graphicImage和StreamedContent显示数据库中的动态图像
@BalusC,对于p:fileDownload
,是否有一种方法可以卸载StreamedContent的创建到另一个对象,然后可以直接从JSF调用?类似于这里卸载p:graphicImage
的方式。如果是这样,这个特殊对象的范围是什么?我猜RequestScoped,因为initDownload和getDownload之间没有连接。ApplicationScoped将无法跟踪单个会话中的所有下载,对吗?我也想知道是否在每个请求中创建一个新的Apache FOP对象太昂贵了?
下面是一个例子:
jsf:<h:commandButton value="print/download" action="#{streamhelper.initDownload()}">
<p:fileDownload value="#{streamhelper.download}"/>
<f:param name="html" value="#{bean.html}" />
<f:param name="idNum" value="#{bean.idNum}" />
</h:commandButton>
特殊对象:
@Named("streamhelper") @RequestScoped @Getter @Setter @Slf4j
public class StreamedContentHelper
{
@PostConstruct @SneakyThrows({NamingException.class})
public void init(){
fop = util.getLocator().getObject(util.getLocator().prependPortableName(FOPEngineImpl.class.getSimpleName()));
}
public void initDownload() throws Exception
{
FacesContext context = FacesContext.getCurrentInstance();
log.trace("context PhaseID: {}", context.getCurrentPhaseId());
String html = context.getExternalContext().getRequestParameterMap().get("html");
String idNum = context.getExternalContext().getRequestParameterMap().get("idNum");
byte[] attachBytes = fop.getPDFBytes(html);
InputStream stream = new ByteArrayInputStream(attachBytes);
stream.mark(0); //remember to this position!
String filename = String.format("%s-download.pdf", loadNum);
download = new DefaultStreamedContent(stream, "application/pdf", filename);
}
private StreamedContent download;
private FOPEngineLocal fop;
private @Inject Util util;
}