Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h3>Introduction</h3> <p>You can get everything through <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/ExternalContext.html" rel="noreferrer"><code>ExternalContext</code></a>. In JSF 1.x, you can get the raw <code>HttpServletResponse</code> object by <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/ExternalContext.html#getResponse%28%29" rel="noreferrer"><code>ExternalContext#getResponse()</code></a>. In JSF 2.x, you can use the bunch of new delegate methods like <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/ExternalContext.html#getResponseOutputStream%28%29" rel="noreferrer"><code>ExternalContext#getResponseOutputStream()</code></a> without the need to grab the <code>HttpServletResponse</code> from under the JSF hoods. </p> <p>On the response, you should set the <code>Content-Type</code> header so that the client knows which application to associate with the provided file. And, you should set the <code>Content-Length</code> header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the <code>Content-Disposition</code> header to <code>attachment</code> if you want a <em>Save As</em> dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.</p> <p>Most important part is to call <a href="http://docs.oracle.com/javaee/7/api/javax/faces/context/FacesContext.html#responseComplete%28%29" rel="noreferrer"><code>FacesContext#responseComplete()</code></a> to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an <code>IllegalStateException</code> with a message like <code>getoutputstream() has already been called for this response</code> when the JSF implementation calls <code>getWriter()</code> to render HTML.</p> <h3>Generic JSF 2.x example</h3> <pre><code>public void download() throws IOException { FacesContext fc = FacesContext.getCurrentInstance(); ExternalContext ec = fc.getExternalContext(); ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide. ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename. ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown. ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead. OutputStream output = ec.getResponseOutputStream(); // Now you can write the InputStream of the file to the above OutputStream the usual way. // ... fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed. } </code></pre> <h3>Generic JSF 1.x example</h3> <pre><code>public void download() throws IOException { FacesContext fc = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse(); response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide. response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename. response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown. response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead. OutputStream output = response.getOutputStream(); // Now you can write the InputStream of the file to the above OutputStream the usual way. // ... fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed. } </code></pre> <h3>Common static file example</h3> <p>In case you need to stream a static file from the local disk file system, substitute the code as below:</p> <pre><code>File file = new File("/path/to/file.ext"); String fileName = file.getName(); String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName); int contentLength = (int) file.length(); // ... Files.copy(file.toPath(), output); </code></pre> <h3>Common dynamic file example</h3> <p>In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide <code>output</code> there where the API being used expects an <code>OutputStream</code>.</p> <p>E.g. iText PDF:</p> <pre><code>String fileName = "dynamic.pdf"; String contentType = "application/pdf"; // ... Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, output); document.open(); // Build PDF content here. document.close(); </code></pre> <p>E.g. Apache POI HSSF:</p> <pre><code>String fileName = "dynamic.xls"; String contentType = "application/vnd.ms-excel"; // ... HSSFWorkbook workbook = new HSSFWorkbook(); // Build XLS content here. workbook.write(output); workbook.close(); </code></pre> <p>Note that you cannot set the content length here. So you need to remove the line to set response content length. This is technically no problem, the only disadvantage is that the enduser will be presented an unknown download progress. In case this is important, then you really need to write to a local (temporary) file first and then provide it as shown in previous chapter.</p> <h3>Turn off ajax!</h3> <p>You only need to make sure that the action method is <strong>not</strong> called by an ajax request, but that it is called by a normal request as you fire with <code>&lt;h:commandLink&gt;</code> and <code>&lt;h:commandButton&gt;</code>. Ajax requests are handled by JavaScript which in turn has, due to security reasons, no facilities to force a <em>Save As</em> dialogue with the content of the ajax response.</p> <p>In case you're using e.g. PrimeFaces <code>&lt;p:commandXxx&gt;</code>, then you need to make sure that you explicitly turn off ajax via <code>ajax="false"</code> attribute. In case you're using ICEfaces, then you need to nest a <code>&lt;f:ajax disabled="true" /&gt;</code> in the command component.</p> <h3>Utility method</h3> <p>If you're using JSF utility library <a href="http://omnifaces.org" rel="noreferrer">OmniFaces</a>, then you can use one of the three convenient <a href="http://omnifaces.org/docs/javadoc/current/org/omnifaces/util/Faces.html#sendFile-java.io.File-boolean-" rel="noreferrer"><code>Faces#sendFile()</code></a> methods taking either a <code>File</code>, or an <code>InputStream</code>, or a <code>byte[]</code>, and specifying whether the file should be downloaded as an attachment (<code>true</code>) or inline (<code>false</code>).</p> <pre><code>public void download() throws IOException { Faces.sendFile(file, true); } </code></pre> <p>Yes, this code is complete as-is. You don't need to invoke <code>responseComplete()</code> and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find <a href="https://github.com/omnifaces/omnifaces/blob/2.0/src/main/java/org/omnifaces/util/FacesLocal.java#L1350" rel="noreferrer">source code here</a>.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload