10

I would like to build a results page for a report export page. This results page must display the status of the export and offer the download of this export.

The export is done in an action method. I can execute it via a commandButton but it must be executed automatically on load.

How can I accomplish this?

JSF:

<h:commandButton value="Download report" action="#{resultsView.downloadReport}"/>

Backing bean:

  public String downloadReport() {
    ...
    FileDownloadUtil.downloadContent(tmpReport, REPORT_FILENAME);
    // Stay on this page
    return null;
  }

Clarification: Is this feasible with a4j? I thought of a solution that an Ajax request triggers my downloadReport action and its request is the file download.

guerda
  • 23,388
  • 27
  • 97
  • 146

4 Answers4

15

You can also solve this in JSF 2.0 using component system events, specifically the PreRenderViewEvent.

Just create a download view (/download.xhtml) that fires a download listener before render.

<?xml version="1.0" encoding="UTF-8"?>
<f:view
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core">
    <f:event type="preRenderView" listener="#{reportBean.download}"/>
</f:view>

Then, in your report bean (defined using JSR-299), you push the file and mark the response as complete.

public @Named @RequestScoped class ReportBean {

   public void download() throws Exception {
      FacesContext ctx = FacesContext.getCurrentInstance();
      pushFile(
           ctx.getExternalContext(),
           "/path/to/a/pdf/file.pdf",
           "file.pdf"
      ); 
      ctx.responseComplete();
   }

   private void pushFile(ExternalContext extCtx,
         String fileName, String displayName) throws IOException {
      File f = new File(fileName);
      int length = 0; 
      OutputStream os = extCtx.getResponseOutputStream();
      String mimetype = extCtx.getMimeType(fileName);

      extCtx.setResponseContentType(
         (mimetype != null) ? mimetype : "application/octet-stream");
      extCtx.setResponseContentLength((int) f.length());
      extCtx.setResponseHeader("Content-Disposition",
         "attachment; filename=\"" + displayName + "\"");

      // Stream to the requester.
      byte[] bbuf = new byte[1024];
      DataInputStream in = new DataInputStream(new FileInputStream(f));

      while ((in != null) && ((length = in.read(bbuf)) != -1)) {
         os.write(bbuf, 0, length);
      }  

      in.close();
   }
}

That's all there is to it!

You can either link to the download page (/download.jsf), or use a HTML meta tag to redirect to it on a splash page.

Dan Allen
  • 2,418
  • 1
  • 16
  • 13
  • I have try this solution. It works only if the page have no other control. If the page have other control like dropdown box selection, and the page is back and forth few times, the `download()` will keep calling and the value will forever reset. Thus, is there a way to guard its execution to only one time forever? – huahsin68 Aug 30 '12 at 02:33
8

The previous answer will submit the form and perhaps change the navigation.

Use <rich:jsFunction action="#{bean.action}" name="loadFunction" /> and then window.onload = loadFunction;

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
3

You can send only one response per request. You cannot send two responses (the page itself and the download file) per request. Best what you can do is to use Javascript to submit a (hidden) form after page load.

window.onload = function() {
    document.formname.submit();
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • +1. Will work, but I'd like to use a4j for this, sorry for not mentioning (see clarification edit). – guerda Nov 06 '09 at 12:38
0

Use events

<ui:composition 
            xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
>
   <f:event type="preRenderView" listener="#{beanName.method}"/>
   ...    
</ui:composition>
  • Why is this better than the other solution? Or are there any usecases where it is more appropriate than the others? – Kukeltje Jun 09 '18 at 18:23