Введение
Вы можете пройти через все ExternalContext
. В JSF 1.x необработанный HttpServletResponse
объект можно получить с помощью ExternalContext#getResponse()
. В JSF 2.x вы можете использовать кучу новых методов делегата, например, ExternalContext#getResponseOutputStream()
без необходимости извлекать их HttpServletResponse
из-под капотов JSF.
В ответе вы должны установить Content-Type
заголовок, чтобы клиент знал, какое приложение нужно связать с предоставленным файлом. И вы должны установить Content-Length
заголовок, чтобы клиент мог рассчитать прогресс загрузки, иначе он будет неизвестен. И вы должны установить Content-Disposition
заголовок, attachment
если вы хотите диалоговое окно « Сохранить как », иначе клиент попытается отобразить его встроенным. Наконец, просто запишите содержимое файла в выходной поток ответа.
Наиболее важной частью является вызов, FacesContext#responseComplete()
чтобы сообщить JSF, что он не должен выполнять навигацию и рендеринг после того, как вы записали файл в ответ, иначе конец ответа будет загрязнен HTML-содержимым страницы или в более старых версиях JSF. , вы получите IllegalStateException
сообщение, например, getoutputstream() has already been called for this response
когда реализация JSF вызывает getWriter()
отображение HTML.
Отключите ajax / не используйте удаленную команду!
Вам нужно только убедиться, что метод действия не вызывается запросом ajax, а вызывается обычным запросом, когда вы запускаете с помощью <h:commandLink>
и <h:commandButton>
. Запросы Ajax и удаленные команды обрабатываются JavaScript, который, в свою очередь, из соображений безопасности не имеет средств для принудительного создания диалога « Сохранить как» с содержимым ответа ajax.
Если вы используете, например, PrimeFaces <p:commandXxx>
, вам нужно убедиться, что вы явно отключили ajax через ajax="false"
атрибут. Если вы используете ICEfaces, вам нужно вложить a <f:ajax disabled="true" />
в командный компонент.
Общий пример 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();
}
Общий пример 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();
}
Пример распространенного статического файла
Если вам нужно передать статический файл из файловой системы локального диска, замените код, как показано ниже:
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, просто укажите 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();
document.close();
Например, Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.write(output);
workbook.close();
Обратите внимание, что здесь нельзя установить длину содержимого. Поэтому вам нужно удалить строку, чтобы установить длину содержимого ответа. Технически это не проблема, единственный недостаток заключается в том, что конечному пользователю будет представлен неизвестный прогресс загрузки. Если это важно, вам действительно нужно сначала записать в локальный (временный) файл, а затем предоставить его, как показано в предыдущей главе.
Утилитарный метод
Если вы используете служебную библиотеку JSF OmniFaces , вы можете использовать один из трех удобных Faces#sendFile()
методов, взяв a File
, или an InputStream
, или a byte[]
, и указав, должен ли файл загружаться как вложение ( true
) или inline ( false
).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Да, этот код является полным как есть. Не нужно призывать себя responseComplete()
и тд. Этот метод также правильно работает с заголовками IE и именами файлов UTF-8. Вы можете найти исходный код здесь .
InputStream
инфраструктураp:fileDownload
, и я не сумел преобразоватьOutputStream
вInputStream
. Теперь ясно, что даже прослушиватель действий может изменить тип содержимого ответа, и тогда ответ в любом случае будет учитываться как загрузка файла на стороне пользовательского агента. Спасибо!preRenderView
слушателя в безупречном виде.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(); }
источник
вот полный фрагмент кода 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(); } }
Вы можете изменить логику чтения файла, если хотите, чтобы файл создавался во время выполнения.
источник