Как обеспечить загрузку файла из компонента поддержки JSF?

Есть ли способ обеспечить загрузку файла с помощью метода действий со стороны компонента JSF? Я много пробовал. Основная проблема заключается в том, что я не могу понять, как получить OutputStream ответа, чтобы записать содержимое файла. Я знаю, как это сделать с помощью Servlet , но это невозможно вызвать из формы JSF и требует нового запроса.

Как я могу получить OutputStream ответа от текущего FacesContext ?

Введение

Вы можете получить все через ExternalContext . В JSF 1.x вы можете получить исходный объект HttpServletResponse помощью ExternalContext#getResponse() . В JSF 2.x вы можете использовать кучу новых методов делегатов, таких как ExternalContext#getResponseOutputStream() без необходимости захватывать HttpServletResponse из-под капотов JSF.

В ответ вам следует установить заголовок Content-Type чтобы клиент знал, какое приложение должно связываться с предоставленным файлом. И вы должны установить заголовок Content-Length чтобы клиент мог вычислить ход загрузки, иначе он будет неизвестен. И вы должны установить заголовок Content-Disposition attachment если вы хотите использовать диалоговое окно « Сохранить как », иначе клиент попытается отобразить его в строке. Наконец, просто напишите содержимое файла в stream вывода ответа.

Наиболее важной частью является вызов FacesContext#responseComplete() чтобы сообщить JSF, что он не должен выполнять навигацию и визуализацию после того, как вы напишете файл для ответа, иначе конец ответа будет загрязнен содержимым HTML-страницы, или в более старых версиях JSF вы получите IllegalStateException с сообщением типа getoutputstream() has already been called for this response когда реализация JSF вызывает getWriter() для визуализации HTML.

Общий пример 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. } 

Общий пример 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. } 

Пример обычного статического файла

Если вам необходимо передать статический файл из локальной файловой системы на диске, замените код следующим образом:

 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); 

Пример общего динамического файла

Если вам необходимо передать динамически сгенерированный файл, такой как PDF или XLS, просто OutputStream output там, где используемый API ожидает OutputStream .

Например, 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(); 

Например, 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(); 

Обратите внимание, что вы не можете установить длину содержимого здесь. Поэтому вам нужно удалить строку, чтобы задать длину содержимого ответа. Это технически не проблема, единственным недостатком является то, что у конечного пользователя будет неизвестный процесс загрузки. В случае, если это важно, вам действительно нужно сначала записать в локальный (временный) файл, а затем предоставить его, как показано в предыдущей главе.

Отключите ajax!

Вам нужно только убедиться, что метод действия не вызывается с помощью запроса ajax, но он вызывается обычным запросом при с помощью и . Запросы Ajax обрабатываются JavaScript, который, в свою очередь, по соображениям безопасности не имеет возможности принудительно активировать диалог « Сохранить как» с содержимым ответа ajax.

Если вы используете, например, PrimeFaces , вам нужно убедиться, что вы явно отключили ajax через атрибут ajax="false" . Если вы используете ICEfaces, вам нужно вложить в компонент команды .

Метод полезности

Если вы используете библиотеку служебных программ JSF OmniFaces , вы можете использовать один из трех удобных методов Faces#sendFile() принимающих либо File , либо InputStream , либо byte[] , а также указать, должен ли файл загружаться как ( true ) или inline ( false ).

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

Да, этот код завершен как есть. Вам не нужно ссылаться на responseComplete() и так далее. Этот метод также правильно обрабатывает заголовки IE и имена файлов UTF-8. Здесь вы можете найти исходный код .

 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(); } 

Это то, что сработало для меня:

 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(); } 

вот полный fragment кода 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(); } } на  @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(); } } 

Вы можете изменить логику чтения файлов, если вы хотите, чтобы файл генерировался во время выполнения.

  • Как загрузить файлы на сервер с помощью JSP / Servlet и Ajax?
  • Как загрузить и сохранить файл из Интернета с помощью Java?
  • Предварительный просмотр изображения перед загрузкой Angularjs
  • Загрузка файла Symfony2
  • Использование подстановочных знаков в запросе wget или curl
  • ASP.NET Как передать файл пользователю
  • Как изменить таймаут на объекте .NET WebClient
  • Загрузите и установите версию Xcode 10 без учетной записи разработчика Premium
  • Загрузить файл на FTP-сервер на iPhone
  • Это приложение не имеет явного отображения для / error
  • escapeshellarg () отключен по соображениям безопасности
  • Давайте будем гением компьютера.