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.
Our sample application project structure looks like this:
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.
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
to
I tried your code but it did not exactly work:
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
The file is not there, your JBoss is throwing 404.
HTTP Status 404 - /bioinvindex/gene_data_download/filea/filea_ge.txt
Nice example, but the target file name is taken from the last qualifier (ViewFileFetch.jsf) in my case. How do I change the file name the browser will use when it saves the file?
Lance
Ah, I forgot my old servlet skills. The answer is to set the “Content-Disposition” in the response header. In my case:
Great tutorial, thank you! You really saved my day))
Kindly replace J2EE with JEE.
J2EE is no longer in use.