1

I need to show a link to download a file on a page in a JSF 2 application. Now, the issue is that the file contains data that depends on fresh looks into the database at the time of creation. But what I want to do is create it AND give the user a link to download it in one action.

This would be very simple to do in two actions: have a button to generate the file, and replace it with a link to download the file once it's generated.

So the question again is can this be done in one click on a commandLink?

EDIT, following BalusC's comment below. Here's the specifics of what I am trying to do, and have done so far. I have this in the xhtml:

<h:panelGroup rendered="#{!bean.showLinkToExcelFile()}">
     <h:form>
         <td><h:commandButton value="Generate list of your Bids for download" action="#{bean.createBidsList()}"/></td>      
    </h:form>
</h:panelGroup>
<h:panelGroup rendered="#{bean.showLinkToExcelFile()}">
    <td><a href="#{bean.findBidsListFileName()}">Download Your Bids</a></td>
</h:panelGroup> 

This works. The button creates an excel file, saves it to a location, and updates the file name in the database. Then the link serves the file. But it's a two step process for the user. What I'd like it to be is just one step. So that one link, for example:

<a href="#{bean.findBidsListFileName()}">Download Your Bids</a>

or, most likely, a jsf commandLink will create the excel file on the back end, save it to the /resources/ location, and seamlessly open a "save" dialog on the user's machine.

Herzog
  • 923
  • 1
  • 14
  • 28
  • The answer is: yes. Do you have more questions? Please show and elaborate where exactly you're stucking while implementing the requirement accordingly. – BalusC Jan 24 '12 at 18:03

2 Answers2

5

You can just let JSF immediately write the report to the OutputStream of the HTTP response instead of to the OutputStream of the local disk file system.

Here's a basic example, assuming that you're using Apache POI to create the Excel report:

public void createAndDownloadBidsReport() throws IOException {
    // Prepare Excel report to be downloaded.
    HSSFWorkbook bidsReport = createBidsReportSomehow();
    String filename = "bids.xls";

    // Prepare response to show a Save As dialogue with Excel report.
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.setResponseContentType("application/vnd.ms-excel");
    externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");

    // Write Excel report to response body.
    bidsReport.write(externalContext.getResponseOutputStream());

    // Inform JSF that response is completed and it thus doesn't have to navigate.
    facesContext.responseComplete();
}

This way you can end up with just a single command link (or button).

<h:form>
    <h:commandLink value="Create and download bids report" action="#{bean.createAndDownloadBidsReport}" />
</h:form>

Note that this doesn't save it to disk file system. If you really need to have a copy then you'd need to add another bidsReport.write() line which writes to the desired disk file system location.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • BalusC, I edited the question with something of a bug I have. If it ends up irrelevant to the current question I'll delete it. Thanks. – Herzog Jan 25 '12 at 16:50
  • 1
    Do you have a `facesContext.responseComplete();` line to inform JSF that it doesn't have to navigate? (read: that it shouldn't write HTML to it). – BalusC Jan 25 '12 at 16:58
  • Never mind, I walked away from the computer and figured it out. I forgot to call facesContext.responseComplete(); – Herzog Jan 25 '12 at 17:02
  • Oh, just saw your comment. Thanks :) – Herzog Jan 25 '12 at 17:03
  • It works but after downloading the file, it throws exception: JBWEB000236: Servlet.service() for servlet FacesServlet threw exception: java.lang.IllegalStateException: JBWEB000028: getOutputStream() has already been called for this response – emeraldhieu Dec 09 '14 at 04:25
0

I would use a normal download link and map the URL to a servlet that generates the document on the fly.

Eelke
  • 20,897
  • 4
  • 50
  • 76