Jochen,
If you are refering to streaming a file- I have struggled with the same thing.
For some reason this breaks the JSF lifecycle. I have however come up with a
solution that uses a hidden iframe. This approach allows JSF to finish its
lifecycle without being affected by the file being streamed for download.
This is how it works...
In your request scoped bean (ReportBean.java) where we want to download a file
from (in my case it was a report) I have a method that invokes the download. It
sets the temporary session attribute that holds the report/file by calling the
static method "FileDownloadBean.setFileForDownload(report);" and sets an
property of the bean that holds the url to the "fileDownload.jsp" using
"setReportDownloadUrl("fileDownload.jsp");" (by default this propertys getter
method returns "about:blank" to the iframe src). When these two methods are
called in your bean the "reportDownloadUrl" getter method will return the
proper url to the "fileDownload.jsp" page invoking the "FileDownloadBean.java"
constructor that invokes the file download process to the iframe. I also added
an error message in case things go wrong. Because we are using a hidden iframe
that has no knowledge of the page that invoked it we can't use the regular
faces messages. However, we can use a simple JavaScript alert that uses a
message resource bundle message.
The source code:
reportResult.jsp (the page we want to download a file from insert the
following- css class "file-download" just sets
"visibility:hidden;display:none;"):
...
<t:panelGroup id="fileDownloadPanel"
binding="#{report.fileDownloadPanel}">
<f:verbatim>
<iframe id="fileDownloadFrame" frameborder="0"
scrolling="0"
src="</f:verbatim><tr:outputText
value="#{report.reportDownloadUrl}" /><f:verbatim>"
class="file-download"></iframe>
</f:verbatim>
</t:panelGroup>
...
fileDownload.jsp (I use myfaces base form and command button, but tr should
work as well):
<f:view>
<f:loadBundle basename="WEB-INF.resources.message" var="labels" />
<t:panelGroup rendered="#{!fileDownload.isComplete}">
<trh:html>
<trh:body onload="#{fileDownload.clientScriptError}">
<h:form id="fFileDownload">
<h:commandButton id="downloadFileBtn"
styleClass="form-inp-button"
binding="#{fileDownload.downloadFileActionSrc}"
value="#{labels.lbl_filedownload_download}"
action="#{fileDownload.downloadFile}"
title="#{labels.lbl_filedownload_download}">
</h:commandButton>
</h:form>
</trh:body>
</trh:html>
</t:panelGroup>
</f:view>
/**
* File download bean used to stream ad hoc files to the client without
* encountering <code>java.lang.IllegalStateException</code>. This still
* occures even when calling
* <code>FacesContext.getCurrentInstance().responseComplete()</code>.
*/
public class FileDownloadBean extends BaseBean {
/**
* Default constructor that downloads a file that has been temporarily
* stored in the session.
*/
public FileDownloadBean() {
downloadFile();
}
/**
* Gets the file for download and passes it to file download method that
* takes an object. This method can be used in a faces page for a file
* download action.
*
* @see FileDownloadBean#downloadFile(Object)
* @return the faces action
*/
public final String downloadFile() {
try {
downloadFile(getFileForDownload());
} catch (Throwable e) {
log.fatal("Unable to call file download", e);
}
return null;
}
/**
* Processes file download
*
* @param file
* @return the faces action
*/
@SuppressWarnings("unchecked")
protected String downloadFile(Object file) {
String errorMessage = null;
try {
if (file == null) {
log.warn("File cannot be null. Ensure that the "
+ "session attribute with key:
" + PARAM_FILE_KEY
+ " is set with a valid file or
init object");
errorMessage = getMessageResourceString(
"bean_filedownload_error_notfound", null, false);
} else if (file instanceof File) {
downloadFile((File) file, true);
} else {
log.error("Invalid file: " + file);
}
} catch (Throwable e) {
log.error("Unable to download the file", e);
errorMessage = getMessageResourceString(
"bean_filedownload_error_unknown",
null, false);
} finally {
completeFileDownload(errorMessage);
}
return null;
}
/**
* Completes the file download process. If the error message is not
null a
* client alert will be initiated to notify the client of the error. If
the
* error message is null the response will be marked as complete.
*
* @param errorMessage
*/
protected final void completeFileDownload(String errorMessage) {
try {
if (errorMessage != null) {
setDownloadErrorScript(errorMessage);
setIsComplete(false);
} else {
responseComplete();
// renderResponse();
setIsComplete(true);
}
setFileDownloadValue(null);
} catch (Throwable e) {
log.error("Unable to complete file download", e);
}
}
/**
* Sets the client download script error text.
*
* @param errorMessage
*/
protected final void setDownloadErrorScript(String errorMessage) {
try {
StringBuffer err = new StringBuffer();
err.append("javascript:");
err.append("alert('");
err.append(isValid(errorMessage) ? errorMessage :
"unknown");
err.append("');");
setClientScriptError(err.toString());
} catch (Throwable e) {
if (log.isWarnEnabled())
log.warn("Unable to set the download error
script: "
+ errorMessage);
}
}
/*------- File Session Methods -------*/
/**
* Sets the temporary session attribute that holds the file to be
streamed
* to the client.
*
* @see FileDownloadBean#setFileDownloadValue(Object)
* @param file
*/
public static final void setFileForDownload(File file) {
setFileDownloadValue(file);
}
/**
* Sets the temporary session attribute that holds the report used to
* generate a streamed report file for downloading.
*
* @see FileDownloadBean#setFileDownloadValue(Object)
* @param report
*/
public static final void setFileForDownload(
IExtendedReport<Report, String, IReportParam> report) {
setFileDownloadValue(report);
}
/*------- Accessor Properties -------*/
/**
* This is the button or link binding that inits the file download
*/
private ActionSource downloadFileActionSrc;
/**
* This is used for checking if the download process is complete
*/
private boolean isComplete;
/**
* Client script error returned when a file download error occurs
*/
private String clientScriptError;
/*------- Accessor Methods -------*/
/**
* @return the clientScriptError
*/
public String getClientScriptError() {
if (clientScriptError == null) {
clientScriptError = "";
}
return clientScriptError;
}
/**
* @param clientScriptError
* the clientScriptError to set
*/
public void setClientScriptError(String clientScriptError) {
this.clientScriptError = clientScriptError;
}
/**
* @return the isComplete
*/
public boolean getIsComplete() {
return isComplete;
}
/**
* @param isComplete
* the isComplete to set
*/
public void setIsComplete(boolean isComplete) {
this.isComplete = isComplete;
}
/**
* @return the downloadFileActionSrc
*/
public ActionSource getDownloadFileActionSrc() {
return downloadFileActionSrc;
}
/**
* @param downloadFileActionSrc
* the downloadFileActionSrc to set
*/
public void setDownloadFileActionSrc(ActionSource
downloadFileActionSrc) {
this.downloadFileActionSrc = downloadFileActionSrc;
}
/*------- Constants -------*/
/**
* log4j
*/
private static final Log log =
LogFactory.getLog(FileDownloadBean.class);
/**
* serialVersionUID
*/
private static final long serialVersionUID = -5803068483496878170L;
}
In a "FacesWebUtil.java" I have the following methods:
/*------- File Download Methods -------*/
/**
* @see FacesWebUtil#downloadFile(FacesContext, File, boolean)
*/
public static final void downloadFile(FacesContext context,
String filePath, String fileName, boolean isAttachment)
throws NullPointerException,
UnsupportedEncodingException,
FileNotFoundException, IOException {
if (log.isDebugEnabled())
log.debug("downloadFile(" + context + ',' + filePath +
','
+ fileName + ',' + isAttachment + ')');
downloadFile(context, new File(filePath, fileName),
isAttachment);
}
/**
* @see FacesWebUtil#downloadFile(FacesContext, BufferedInputStream,
String,
* boolean)
*/
public static final void downloadFile(FacesContext context, File file,
boolean isAttachment) throws
UnsupportedEncodingException,
IOException, FileNotFoundException {
if (log.isDebugEnabled())
log.debug("downloadFile(" + context + ',' + file + ','
+ isAttachment + ')');
downloadFile(context,
new BufferedInputStream(new
FileInputStream(file)), file
.getName(), isAttachment);
}
/**
* Streams a binary input stream to the faces response
*
* @param context
* @param input
* @param isAttachment
* @param fileName
*/
public static final void downloadFile(FacesContext context,
BufferedInputStream input, String fileName, boolean
isAttachment)
throws UnsupportedEncodingException, IOException {
if (log.isDebugEnabled())
log.debug("downloadFile(" + context + ',' + input + ','
+ fileName
+ ',' + isAttachment + ')');
if (input != null) {
BufferedOutputStream output = null;
try {
if (context == null) {
context =
FacesContext.getCurrentInstance();
}
String disposition = isAttachment ?
"attachment" : "inline";
int contentLength = input.available();
// Init servlet response
HttpServletResponse response =
(HttpServletResponse) context
.getExternalContext().getResponse();
response.setContentType("application/octet-stream");
response.setContentLength(contentLength);
response.setHeader("Content-disposition",
disposition
+ "; filename=\"" + fileName +
"\"");
// Write file contents to the response
output = new
BufferedOutputStream(response.getOutputStream());
while (contentLength-- > 0) {
output.write(input.read());
}
output.flush();
context.responseComplete();
// context.renderResponse();
} catch (UnsupportedEncodingException e) {
log.error("Invalid file download encoding");
throw e;
} catch (IOException e) {
log.error("File download i/o exception");
throw e;
} finally {
try {
if (input != null) {
input.close();
}
if (output != null) {
output.close();
}
} catch (Throwable e) {
}
}
} else {
log.error("Input stream cannot be null");
}
}
-----Original Message-----
From: Jochen Traunecker [mailto:[EMAIL PROTECTED]
Sent: Tuesday, March 27, 2007 4:27 AM
To: [email protected]
Subject: Providing Files / Streams as download-link with trinidad?
Hello everybody,
I'm just wondering if there is some Trinidad-specific / Faces-specific way to
serve binary downloads, like PDFs stored in a database. Or do I have to provide
some link to a non managed servlet?
Thanks,
Jochen
___________________________________________________________
Telefonate ohne weitere Kosten vom PC zum PC: http://messenger.yahoo.de