Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download a file stored in a database with JSF 2.0

I need to download a file stored in a database. I think i did correctly the query and called it i just dont know how can i connect that to a button in a JSF page. Also i am wondering, do i have to save that image in a folder at the server before passing it to the JSF page. If so, How can i do that?

This is the query i use to return the byte[] from the db:

@NamedQuery(name = "downloadGarbage", query = "SELECT g.file FROM Garbage g WHERE g.id :idParam")
@Entity
public class Garbage implements Serializable {
@Lob
@Column(nullable = false)
private byte[] file;
....

Here is a simple EJB that calls that query then get the id:

@Stateless(name = "ejbs/FileDownloaderEJB")
public class FileDownloaderEJB implements IFileDownloaderEJB {

@PersistenceContext
private EntityManager em;

public byte[] downloadGarbage(Long id) {
    Query query = em.createNamedQuery("downloadGarbage");
    query.setParameter("idParam", id);

    Object o = query.getSingleResult();
    byte[] tmpArray = (byte[]) o; 
    return tmpArray;
}

Now this is the part that confuses me, how can i that above with the JSF page and managed bean?

@ManagedBean
@RequestScoped
public class DownloadController {

@EJB
private FileDownloaderEJB fileDownloaderEJB;

    ...

    private Garbage garbage;

public void startDownload(Long id) {
    fileDownloaderEJB.downloadGarbage(id);
//HOW TO START THE DOWNLOAD?
//Other Get Set Methods...

}

}

Also how can i pass that long id to the managedBean from the JSF from within a commandButton? Is this allowed?

<!-- How to pass the value of id from -->
<h:commandButton action="downloadController.startDownload(#{garbage.id})">
like image 559
javing Avatar asked Mar 31 '11 10:03

javing


1 Answers

Passing parameters in EL only works when the web.xml is declared as Servlet 3.0 and the servletcontainer also supports it (Glassfish 3, JBoss AS 6, Tomcat 7, etc). There's only a syntax error in your attempt, here's the correct way:

<h:commandButton action="#{downloadController.startDownload(garbage.id)}" />

You can even pass whole objects along, that's better in this particular case.

<h:commandButton action="#{downloadController.startDownload(garbage)}" />

Then, the startDownload() method should set the response headers so that the webbrowser understands what content type the response body represents and what to do with it and finally write the content to the response body. You can do it all with help of ExternalContext. Here's a kickoff example:

public void startDownload(Garbage garbage) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.setResponseHeader("Content-Type", garbage.getContentType());
    externalContext.setResponseHeader("Content-Length", garbage.getContent().length);
    externalContext.setResponseHeader("Content-Disposition", "attachment;filename=\"" + garbage.getFileName() + "\"");
    externalContext.getResponseOutputStream().write(garbage.getContent());
    facesContext.responseComplete();
}

The last line with FacesContext#responseComplete() is mandatory so that JSF understands that it shouldn't navigate to some view and thus potentially malform the response with another JSF page afterwards.

like image 57
BalusC Avatar answered Sep 18 '22 00:09

BalusC