¿Cómo proporcionar una descarga de archivos desde un bean de respaldo JSF?

¿Hay alguna forma de proporcionar una descarga de archivos desde un método de acción de bean de respaldo JSF? He intentado muchas cosas. El problema principal es que no puedo entender cómo obtener el OutputStream de la respuesta para escribir el contenido del archivo. Sé cómo hacerlo con un Servlet , pero esto no se puede invocar desde un formulario JSF y requiere una nueva solicitud.

¿Cómo puedo obtener el OutputStream de la respuesta del FacesContext actual?

Introducción

Puedes obtener todo a través de ExternalContext . En JSF 1.x, puede obtener el objeto HttpServletResponse sin HttpServletResponse por ExternalContext#getResponse() . En JSF 2.x, puede usar el conjunto de nuevos métodos de delegado como ExternalContext#getResponseOutputStream() sin la necesidad de tomar HttpServletResponse de debajo de los HttpServletResponse JSF.

En la respuesta, debe establecer el encabezado Content-Type para que el cliente sepa qué aplicación asociar con el archivo proporcionado. Y debe configurar el encabezado Content-Length para que el cliente pueda calcular el progreso de la descarga, de lo contrario será desconocido. Y, debe establecer el encabezado Content-Disposition en los attachment si desea un cuadro de diálogo Guardar como ; de lo contrario, el cliente intentará mostrarlo en línea. Finalmente, solo escriba el contenido del archivo en la secuencia de salida de respuesta.

La parte más importante es llamar a FacesContext#responseComplete() para informar a JSF que no debe realizar navegación y renderización después de haber escrito el archivo en la respuesta, de lo contrario, el final de la respuesta se verá contaminado con el contenido HTML de la página, o en versiones anteriores de JSF, obtendrá una IllegalStateException con un mensaje como getoutputstream() has already been called for this response cuando la implementación de JSF llama a getWriter() para representar HTML.

Ejemplo genérico de JSF 2.x

 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. } 

Ejemplo genérico de JSF 1.x

 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. } 

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 de la siguiente manera:

 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 dynamic 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 está utilizando 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(); // Build PDF content here. document.close(); 

Por ejemplo, Apache POI HSSF:

 String fileName = "dynamic.xls"; String contentType = "application/vnd.ms-excel"; // ... HSSFWorkbook workbook = new HSSFWorkbook(); // Build XLS content here. 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 duración del contenido de la respuesta. Esto técnicamente 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 primero en un archivo local (temporal) y luego proporcionarlo como se muestra en el capítulo anterior.

¡Apaga ajax!

Solo necesita asegurarse de que el método de acción no sea invocado por una solicitud ajax, sino que sea invocado por una solicitud normal al disparar con y . Las solicitudes de Ajax son manejadas por JavaScript, que a su vez, por razones de seguridad, no tiene instalaciones para forzar un diálogo de Guardar como con el contenido de la respuesta de Ajax.

En caso de que esté utilizando, por ejemplo, PrimeFaces , entonces debe asegurarse de desactivar explícitamente ajax mediante el atributo ajax="false" . En caso de que esté utilizando ICEfaces, debe anidar un en el componente de comando.

Método de utilidad

Si está utilizando la biblioteca de utilidades JSF OmniFaces , puede usar uno de los tres métodos convenientes Faces#sendFile() tomando un File , o un InputStream , o un byte[] , y especificando si el archivo debe descargarse como una adjunto ( true ) o en línea ( false ).

 public void download() throws IOException { Faces.sendFile(file, true); } 

Sí, este código está completo tal como está. No necesita invocar responseComplete() y así sucesivamente. Este método también trata adecuadamente con encabezados específicos de IE y nombres de archivo UTF-8. Puedes encontrar el código fuente aquí .

 public void download() throws IOException { File file = new File("file.txt"); FacesContext facesContext = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse(); response.reset(); response.setHeader("Content-Type", "application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=file.txt"); OutputStream responseOutputStream = response.getOutputStream(); InputStream fileInputStream = new FileInputStream(file); byte[] bytesBuffer = new byte[2048]; int bytesRead; while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0) { responseOutputStream.write(bytesBuffer, 0, bytesRead); } responseOutputStream.flush(); fileInputStream.close(); responseOutputStream.close(); facesContext.responseComplete(); } 

Esto es lo que funcionó para mí:

 public void downloadFile(String filename) throws IOException { final FacesContext fc = FacesContext.getCurrentInstance(); final ExternalContext externalContext = fc.getExternalContext(); final File file = new File(filename); externalContext.responseReset(); externalContext.setResponseContentType(ContentType.APPLICATION_OCTET_STREAM.getMimeType()); externalContext.setResponseContentLength(Long.valueOf(file.lastModified()).intValue()); externalContext.setResponseHeader("Content-Disposition", "attachment;filename=" + file.getName()); final HttpServletResponse response = (HttpServletResponse) externalContext.getResponse(); FileInputStream input = new FileInputStream(file); byte[] buffer = new byte[1024]; final ServletOutputStream out = response.getOutputStream(); while ((input.read(buffer)) != -1) { out.write(buffer); } out.flush(); fc.responseComplete(); } 

aquí está el fragmento de código completo http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/

  @ManagedBean(name = "formBean") @SessionScoped public class FormBean implements Serializable { private static final long serialVersionUID = 1L; /** * Download file. */ public void downloadFile() throws IOException { File file = new File("C:\\docs\\instructions.txt"); InputStream fis = new FileInputStream(file); byte[] buf = new byte[1024]; int offset = 0; int numRead = 0; while ((offset < buf.length) && ((numRead = fis.read(buf, offset, buf.length -offset)) >= 0)) { offset += numRead; } fis.close(); HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance() .getExternalContext().getResponse(); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=instructions.txt"); response.getOutputStream().write(buf); response.getOutputStream().flush(); response.getOutputStream().close(); FacesContext.getCurrentInstance().responseComplete(); } } 

Puede cambiar la lógica de lectura del archivo en caso de que desee generar un archivo en tiempo de ejecución.

    Intereting Posts