如何使用流内容与p:fileDownload下载非类路径文件



我用的是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;
}

相关内容

  • 没有找到相关文章