JSF, Ajax and file download


Today I show you a example which combines Ajax (Richfaces) and a „normal“ download in a JSF application.

Here is the bean with the download-code:

import java.io.File;
import java.io.IOException;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.http.HttpServletResponse;

public class DownloadBean {

  // actionListener="#{bean.execute}"
  public void execute (ActionEvent event) {
   download();
  }

  // action="#{bean.download}"
  public String download() {
    final FacesContext facesContext = FacesContext.getCurrentInstance();
    // XXX create temp. filecontent ... resultFile
    writeOutContent(getServletResponse(), new File(this.resultFile), "file.xml");
    facesContext.responseComplete();
    // dont use jsf-navigation-rules
    return null;
  }

  void writeOutContent(final HttpServletResponse res, final File content, final String theFilename) {
    if (content == null)
      return;
    try {
      res.setHeader("Pragma", "no-cache");
      res.setDateHeader("Expires", 0);
      res.setContentType("text/xml");
      res.setHeader("Content-disposition", "attachment; filename=" + theFilename);
      fastChannelCopy(Channels.newChannel(new FileInputStream(content)), Channels.newChannel(res.getOutputStream()));
    } catch (final IOException e) {
      // TODO produce a error message🙂
    }
  }

  void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
    while (src.read(buffer) != -1) {
      buffer.flip();
      dest.write(buffer);
      buffer.compact();
    }
    buffer.flip();
    while (buffer.hasRemaining()){
      dest.write(buffer);
    }
  }
}

Now the facelets snipplets: Not Working – Javascript Error😦

<h:form>
 <h:commandLink actionListener="#{command.execute}" value="Download"/>
 <h:commandLink action="#{command.download}" value="Download"/>
</h:form>

Not Working – Download is shown in the current page – the ajax page will replaced with the download-content😦

<a4j:form>
  <a4j:commandLink actionListener="#{command.execute}" value="Download"/>
  <a4j:commandLink action="#{command.download}" value="Download"/>
</a4j:form>

Working🙂

<a4j:form>
 <a4j:htmlCommandLink actionListener="#{command.execute}" value="Download"/>
 <a4j:htmlCommandLink action="#{command.download}" value="Download"/>
</a4j:form>
<h:form>
 <a4j:htmlCommandLink actionListener="#{command.execute}" value="Download"/>
 <a4j:htmlCommandLink action="#{command.download}" value="Download"/>
</h:form>

The a4j:htmlCommandLink was made for this szenario.

Notice: the fast channel copy routine was introduced by waffel.