BRabbit27
BRabbit27

Reputation: 6623

Download file from primefaces tree

This is my xhtml-code

        <p:tree value="#{documentsController.root}"
                var="node" selectionMode="single"
                dynamic="true">

            <p:treeNode 
                expandedIcon="ui-icon-folder-open"
                collapsedIcon="ui-icon-folder-collapsed">
                <h:outputText value="#{node}" />
            </p:treeNode>

            <p:treeNode type="file" icon="ui-icon-document">
                <h:outputText value="#{node}"/>
            </p:treeNode>

            <p:ajax event="select" listener="#{documentsController.onNodeSelect}"/>

        </p:tree>

and this is the backing bean

@ManagedBean
@ViewScoped
public class DocumentsController implements Serializable {

    TreeNode root;
    //TreeNode[] selectedNodes;

    @PostConstruct
    public void init() {

        root = new DefaultTreeNode("SRC", null);

        TreeNode node0 = new DefaultTreeNode("A", root);
        TreeNode node1 = new DefaultTreeNode("B", root);
        TreeNode node2 = new DefaultTreeNode("C", root);

        TreeNode node3 = new DefaultTreeNode("file", "D", node0);
        TreeNode node4 = new DefaultTreeNode("file", "E", node0);
        TreeNode node5 = new DefaultTreeNode("file", "F", node0);

        String p = "C:\\Users\\federico.martinez\\Desktop\\a.wmv";

        TreeNode node6 = new DefaultTreeNode("file", new File(p), node1);
        TreeNode node7 = new DefaultTreeNode("file", "h", node1);
        TreeNode node8 = new DefaultTreeNode("file", "i", node1);

    }

    public TreeNode getRoot() {
        return root;
    }

    /*
    public void setSelectedNodes(TreeNode[] selectedNodes){
    this.selectedNodes = selectedNodes;
    }

    public TreeNode[] getSelectedNodes(){
    return selectedNodes;
    }*/
    public void onNodeSelect(NodeSelectEvent event) {
        if (event.getTreeNode().getType().equals("file")) {
            File file = new File(event.getTreeNode().getData().toString());
            FacesContext facesContext = FacesContext.getCurrentInstance();
            ExternalContext externalContext = facesContext.getExternalContext();

            externalContext.setResponseHeader("Content-Type", externalContext.getMimeType(file.getName()));
            externalContext.setResponseHeader("Content-Length", String.valueOf(file.length()));
            externalContext.setResponseHeader("Content-Disposition", "attachment;filename=\"" + file.getName() + "\"");

            InputStream input = null;
            OutputStream output = null;

            try {
                input = new FileInputStream(file);
                output = externalContext.getResponseOutputStream();
                IOUtils.copy(input, output);
            } catch (FileNotFoundException ex) {
                System.out.println("FileNotFound: " + ex.getMessage());
            } catch (IOException ex) {
                System.out.println("IO: " + ex.getMessage());
            } finally {
                IOUtils.closeQuietly(output);
                IOUtils.closeQuietly(input);
            }
            facesContext.responseComplete();
        }
    }
}

But it is not downloading the file, I used this download method with the Tomahawk tree and it worked, now I'm trying with PrimeFaces but this doesn't download the file and no error!

any advise what could be wrong?

Thanks in advance !

UPDATE

I have this modification but can't make it download the file.

        <p:tree value="#{documentsController.root}"
                var="node" selectionMode="single"
                dynamic="true">

            <p:treeNode 
                expandedIcon="ui-icon-folder-open"
                collapsedIcon="ui-icon-folder-collapsed">
                <h:outputText value="#{node}" />
            </p:treeNode>

            <p:treeNode type="file" icon="ui-icon-document">
                <p:commandButton value="#{node}" ajax="false">
                    <p:fileDownload value="#{documentsController.download(node)}" />
                </p:commandButton>
            </p:treeNode>

        </p:tree>

and the backbean is

public void download(String path) {

    File f = new File(path);
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();

    InputStream is = ((ServletContext)externalContext.getContext()).getResourceAsStream(path);

    file = new DefaultStreamedContent(is, externalContext.getMimeType(f.getName()), f.getName());   

}

public StreamedContent getFile(){
    return file;
}

Upvotes: 0

Views: 5893

Answers (1)

BalusC
BalusC

Reputation: 1108642

Your first problem is that you're attempting to download a file by an Ajax request. This is not possible. Ajax is executed and managed by JavaScript code. JavaScript has due to security restrictions no facilities to trigger a Save As dialogue. You need to fire a fullworthy synchronous request instead. The original page will remain the same anyway if the content disposition is set to attachment.

So, you need at least a <h:commandButton> or <p:commandButton ajax="false">.

Your second problem which you got when you attempted to use <p:fileDownload> is that the value must point to a method returning StreamedContent, not void. You've bound it to a void method and hence nothing will be returned.

So, you need to return StreamedContent on <p:fileDownload> value.

Your third problem is that you're getting the file as classpath resource by ServletContext#getResourceAsStream() (which I believe you blindly copypasted from PrimeFaces showcase example, which in turn is by itself also pretty poor, it could just have used ExternalContext#getResourceAsStream() instead without the need to grab ServletContext from under the JSF's hoods, but that aside), instead of FileInputStream as in your original code.

So, either of those solutions must help:

<p:treeNode type="file" icon="ui-icon-document">
    <p:commandButton value="#{node}" ajax="false">
        <p:fileDownload value="#{documentsController.download(node)}" />
    </p:commandButton>
</p:treeNode>

with

public StreamedContent download(String path) {
    File file = new File(path);
    InputStream input = new FileInputStream(file);
    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
    return new DefaultStreamedContent(input, externalContext.getMimeType(file.getName()), file.getName());   
}

or

<p:treeNode type="file" icon="ui-icon-document">
    <h:commandButton value="#{node}" action="#{documentsController.download(node)}" />
</p:treeNode>

with your original onNodeSelect() method with the argument changed to String path.

Upvotes: 2

Related Questions