Introducción
Puede pasar todo ExternalContext
. En JSF 1.x, puede obtener el HttpServletResponse
objeto sin formato ExternalContext#getResponse()
. En JSF 2.x, puede usar un montón de nuevos métodos de delegado como ExternalContext#getResponseOutputStream()
sin la necesidad de tomar el HttpServletResponse
de debajo de las campanas JSF.
En la respuesta, debe configurar el Content-Type
encabezado para que el cliente sepa qué aplicación asociar con el archivo proporcionado. Y debe configurar el Content-Length
encabezado para que el cliente pueda calcular el progreso de la descarga, de lo contrario será desconocido. Y debe establecer el Content-Disposition
encabezado en attachment
si desea un cuadro de diálogo Guardar como ; de lo contrario, el cliente intentará mostrarlo en línea. Finalmente, simplemente escriba el contenido del archivo en el flujo de salida de respuesta.
La parte más importante es llamar FacesContext#responseComplete()
para informar a JSF que no debe realizar la navegación y el renderizado después de haber escrito el archivo en la respuesta; de lo contrario, el final de la respuesta se contaminará con el contenido HTML de la página o en versiones JSF anteriores. , obtendrá un IllegalStateException
mensaje con un mensaje como getoutputstream() has already been called for this response
cuando la implementación JSF llama getWriter()
a renderizar HTML.
¡Apague ajax / no use el comando remoto!
Solo necesita asegurarse de que el método de acción no sea llamado por una solicitud ajax, sino que sea llamado por una solicitud normal al disparar con <h:commandLink>
y <h:commandButton>
. Las solicitudes de Ajax y los comandos remotos son manejados por JavaScript que a su vez, debido a razones de seguridad, no tiene facilidades para forzar un diálogo Guardar como con el contenido de la respuesta ajax.
En caso de que esté utilizando, por ejemplo <p:commandXxx>
, PrimeFaces , debe asegurarse de desactivar explícitamente ajax a través del ajax="false"
atributo. En caso de que esté usando ICEfaces, entonces necesita anidar un <f:ajax disabled="true" />
en el componente de comando.
Ejemplo genérico de JSF 2.x
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
ec.responseReset();
ec.setResponseContentType(contentType);
ec.setResponseContentLength(contentLength);
ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
OutputStream output = ec.getResponseOutputStream();
fc.responseComplete();
}
Ejemplo genérico de JSF 1.x
public void download() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();
response.reset();
response.setContentType(contentType);
response.setContentLength(contentLength);
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
OutputStream output = response.getOutputStream();
fc.responseComplete();
}
Ejemplo de archivo estático común
En caso de que necesite transmitir un archivo estático desde el sistema de archivos del disco local, sustituya el código como se muestra a continuación:
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);
Ejemplo de archivo dinámico común
En caso de que necesite transmitir un archivo generado dinámicamente, como PDF o XLS, simplemente proporcione output
allí donde la API que se utiliza espera un OutputStream
.
Por ejemplo, iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
document.close();
Por ejemplo, Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.write(output);
workbook.close();
Tenga en cuenta que no puede establecer la longitud del contenido aquí. Por lo tanto, debe eliminar la línea para establecer la longitud del contenido de la respuesta. Técnicamente, esto no es un problema, la única desventaja es que al usuario final se le presentará un progreso de descarga desconocido. En caso de que esto sea importante, entonces realmente necesita escribir en un archivo local (temporal) primero y luego proporcionarlo como se muestra en el capítulo anterior.
Método de utilidad
Si está utilizando la biblioteca de utilidades JSF OmniFaces , puede usar uno de los tres Faces#sendFile()
métodos convenientes tomando a File
, o an InputStream
, o a byte[]
, y especificando si el archivo debe descargarse como un archivo adjunto ( true
) o en línea ( false
).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Sí, este código está completo tal cual. No es necesario que se invoque, responseComplete()
etc. Este método también trata adecuadamente con encabezados específicos de IE y nombres de archivo UTF-8. Puede encontrar el código fuente aquí .
InputStream
infraestructurap:fileDownload
y no he logrado convertirOutputStream
aInputStream
. Ahora está claro que incluso un oyente de acciones puede cambiar el tipo de contenido de la respuesta y, de todos modos, la respuesta se respetará como una descarga de archivo en el lado del usuario-agente. ¡Gracias!