How to download a file using JSF 2 action

Today we have an interesting use case for you. Our sample JSF 2 application displays a PDF file after button is clicked. We have prepared an example action code which downloads PDF file from a given URL (http://download.itcuties.com/jsf2/jsf2-download-pdf/itcuties-logo-concept.pdf) and displays it’s contents as the browser response to the button click. Here is how it works.

JSF 2 download file - Sample application - Click the Download PDF button to open PDF document

JSF 2 download file – Sample application – Click the Download PDF button to open PDF document

Our sample application project structure looks like this:

JSF 2 download file - Eclipse project structure

JSF 2 download file – Eclipse project structure

Here is the code.

index.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"      
      xmlns:h="http://java.sun.com/jsf/html">
 
    <h:head>
        <title>JSF2 - download PDF after button click</title>
    </h:head>
    <h:body>
    	<h:form>
    		<h2>Click button to download PDF file</h2>
    		<br/>
    		<h:commandButton id="button" value="Download PDF" action="#{downloadBean.downloadPdf}"/>
    	</h:form>
    </h:body>
</html>

This is our sample application’s view. It contains only the commandButton component which calls downloadBean.downloadPdf action. When this button is clicked PDF document is displayed in the browser.

DownloadBean

package com.itcuties.examples.webapps;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.URL;

import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletResponse;

@ManagedBean
public class DownloadBean implements Serializable {

	private static final long serialVersionUID = 626953318628565053L;
	
	// A PDF to download
	private static final String PDF_URL = "http://download.itcuties.com/jsf2/jsf2-download-pdf/itcuties-logo-concept.pdf";
	
	/**
	 * This method reads PDF from the URL and writes it back as a response. 
	 * @throws IOException 
	 */
	public void downloadPdf() throws IOException {
		// Get the FacesContext
		FacesContext facesContext = FacesContext.getCurrentInstance();
		
		// Get HTTP response
		HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
		
		// Set response headers
		response.reset();	// Reset the response in the first place
        response.setHeader("Content-Type", "application/pdf");	// Set only the content type
        
		// Open response output stream
		OutputStream responseOutputStream = response.getOutputStream();
		
		// Read PDF contents
		URL url = new URL(PDF_URL);
        InputStream pdfInputStream = url.openStream();
        
        // Read PDF contents and write them to the output
        byte[] bytesBuffer = new byte[2048];
        int bytesRead;
        while ((bytesRead = pdfInputStream.read(bytesBuffer)) > 0) {
        	responseOutputStream.write(bytesBuffer, 0, bytesRead);
        }
        
        // Make sure that everything is out
        responseOutputStream.flush();
         
        // Close both streams
        pdfInputStream.close();
        responseOutputStream.close();
        
        // JSF doc: 
        // Signal the JavaServer Faces implementation that the HTTP response for this request has already been generated 
        // (such as an HTTP redirect), and that the request processing lifecycle should be terminated
        // as soon as the current phase is completed.
        facesContext.responseComplete();
        
	}	
}

Here is the heart of our sample solution – downloadPdf method of the DownloadBean class. At the beginning of the code we obtain the HttpServletResponse object. Next we need to set Content-Type header of the response to application/pdf because we are returning the PDF contents as the response. Next step is to obtain the OutputStream object. Now we are ready to write response data to it.
Next PDF file bytes are being read the same way we have shown you in the Java Download file tutorial some time ago. PDF bytes are being read in the while loop and written to the response OutputStream.
After the streams are closed there is one important thing to do. You need to signal the JSF that the HTTP response for this request has been generated. You do that by calling the FacesContext.responseComplete() method.

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>JSF 2 - download PDF after the button is clicked</display-name>

	<!-- Welcome page -->
	<welcome-file-list>
		<welcome-file>index.xhtml</welcome-file>
	</welcome-file-list>

	<!-- Staring JSF -->
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- JSF URL mapping -->
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.xhtml</url-pattern>
	</servlet-mapping>
</web-app>

This is the standard configuration of the JSF framework that we use in our tutorials. JSF servlet is configured to load when application starts and mapped to all the *.xhtml URLs of the application. Welcome file is set to the index.xhtml page.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.itcuties.examples.webapps</groupId>
	<artifactId>jsf2-download-pdf</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>jsf2-download-pdf Maven Webapp</name>
	<url>http://www.itcuties.com</url>
	<dependencies>
		<!-- JSF 2 API -->
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-api</artifactId>
			<version>2.2.0-m07</version>
		</dependency>
		<!-- Servlet API -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>jsf2-download-pdf</finalName>
	</build>
</project>

This is our maven configuration for this project.

Download this sample code here.

This code is available on our GitHub repository as well.

2 Responses to "How to download a file using JSF 2 action"

  1. Matthew says:

    Does the solution really depend on the pom.xml and web.xml configuration?
    Also, what if I want to download a .zip file?
    Would I have to rewrite

    response.setHeader("Content-Type", "application/pdf");
    

    to

    response.setHeader("Content-Type", "application/zip");
    

    I tried your code but it did not exactly work:

    10:12:35,147 SEVERE [application] java.io.FileNotFoundException: http://nntc-dcc.unmc.edu/bioinvindex/gene_data_download/filea/filea_ge.txt
    javax.faces.el.EvaluationException: java.io.FileNotFoundException: http://nntc-dcc.unmc.edu/bioinvindex/gene_data_download/filea/filea_ge.txt
            at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
            at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
            at javax.faces.component.UICommand.broadcast(UICommand.java:387)
            at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:329)
    ...
    

    The thing is, I create the file

    http://nntc-dcc.unmc.edu/bioinvindex/gene_data_download/filea/filea_ge.txt

    on the fly, and then want to make it available for downloading. Do I need to set some permissions or something? Like would chmod 600 filename be enough?

    Thanks,
    Matt

    Reply
    • itcuties says:

      The file is not there, your JBoss is throwing 404.
      HTTP Status 404 - /bioinvindex/gene_data_download/filea/filea_ge.txt

      Reply

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Java by Example App is available at Google Play Store NOW