Is there any way of providing a file download from a JSF backing bean action method?
I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream
of the response in order to write the file content to. I know how to do it with a Servlet
, but this cannot be invoked from a JSF form and requires a new request.
How can I get the OutputStream
of the response from the current FacesContext
?
here is the complete code snippet http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
You may change the file reading logic in case you want file to get generated at runtime.
Introduction
You can get everything through
ExternalContext
. In JSF 1.x, you can get the rawHttpServletResponse
object byExternalContext#getResponse()
. In JSF 2.x, you can use the bunch of new delegate methods likeExternalContext#getResponseOutputStream()
without the need to grab theHttpServletResponse
from under the JSF hoods.On the response, you should set the
Content-Type
header so that the client knows which application to associate with the provided file. And, you should set theContent-Length
header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set theContent-Disposition
header toattachment
if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.Most important part is to call
FacesContext#responseComplete()
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 anIllegalStateException
with a message likegetoutputstream() has already been called for this response
when the JSF implementation callsgetWriter()
to render HTML.Turn off ajax / don't use remote command!
You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with
<h:commandLink>
and<h:commandButton>
. Ajax requests and remote commands are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.In case you're using e.g. PrimeFaces
<p:commandXxx>
, then you need to make sure that you explicitly turn off ajax viaajax="false"
attribute. In case you're using ICEfaces, then you need to nest a<f:ajax disabled="true" />
in the command component.Generic JSF 2.x example
Generic JSF 1.x example
Common static file example
In case you need to stream a static file from the local disk file system, substitute the code as below:
Common dynamic file example
In case you need to stream a dynamically generated file, such as PDF or XLS, then simply provide
output
there where the API being used expects anOutputStream
.E.g. iText PDF:
E.g. Apache POI HSSF:
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.
Utility method
If you're using JSF utility library OmniFaces, then you can use one of the three convenient
Faces#sendFile()
methods taking either aFile
, or anInputStream
, or abyte[]
, and specifying whether the file should be downloaded as an attachment (true
) or inline (false
).Yes, this code is complete as-is. You don't need to invoke
responseComplete()
and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.This is what worked for me: