Wprowadzenie
Możesz przejść wszystko ExternalContext
. W JSF 1.x możesz pobrać surowy HttpServletResponse
obiekt przez ExternalContext#getResponse()
. W JSF 2.x możesz użyć wielu nowych metod delegatów, ExternalContext#getResponseOutputStream()
bez potrzeby chwytania ich HttpServletResponse
spod masek JSF.
W odpowiedzi należy ustawić Content-Type
nagłówek, aby klient wiedział, którą aplikację skojarzyć z podanym plikiem. I powinieneś ustawić Content-Length
nagłówek tak, aby klient mógł obliczyć postęp pobierania, w przeciwnym razie będzie on nieznany. I powinieneś ustawić Content-Disposition
nagłówek na, attachment
jeśli chcesz okno dialogowe Zapisz jako , w przeciwnym razie klient spróbuje wyświetlić je w tekście. Na koniec po prostu zapisz zawartość pliku w strumieniu wyjściowym odpowiedzi.
Najważniejszą częścią jest wezwanie FacesContext#responseComplete()
do poinformowania JSF, że nie powinno wykonywać nawigacji i renderowania po zapisaniu pliku do odpowiedzi, w przeciwnym razie koniec odpowiedzi zostanie zanieczyszczony treścią HTML strony lub w starszych wersjach JSF , otrzymasz IllegalStateException
komunikat podobny do tego, getoutputstream() has already been called for this response
gdy implementacja JSF wywołuje getWriter()
renderowanie HTML.
Wyłącz Ajax / nie używaj zdalnego polecenia!
Musisz tylko upewnić się, że metoda akcji nie jest wywoływana przez żądanie AJAX, ale jest wywoływana przez normalne żądanie podczas uruchamiania z <h:commandLink>
i <h:commandButton>
. Żądania Ajax i polecenia zdalne są obsługiwane przez JavaScript, który z kolei, ze względów bezpieczeństwa, nie ma możliwości wymuszenia dialogu Zapisz jako z treścią odpowiedzi Ajax .
W przypadku, gdy używasz np. PrimeFaces <p:commandXxx>
, musisz upewnić się, że jawnie wyłączyłeś Ajax poprzez ajax="false"
atrybut. Jeśli używasz ICEfaces, musisz zagnieździć <f:ajax disabled="true" />
w komponencie poleceń.
Ogólny przykład 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();
}
Ogólny przykład 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();
}
Przykład typowego pliku statycznego
Jeśli chcesz przesłać strumieniowo plik statyczny z lokalnego systemu plików dysku, zastąp kod jak poniżej:
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);
Przykład typowego pliku dynamicznego
Jeśli chcesz przesłać strumieniowo dynamicznie generowany plik, taki jak PDF lub XLS, po prostu podaj output
tam, gdzie używane API oczekuje pliku OutputStream
.
Np. IText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
document.close();
Np. Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.write(output);
workbook.close();
Zwróć uwagę, że nie możesz tutaj ustawić długości treści. Musisz więc usunąć wiersz, aby ustawić długość treści odpowiedzi. Z technicznego punktu widzenia nie stanowi to problemu, jedyną wadą jest to, że użytkownikowi końcowemu zostanie przedstawiony nieznany postęp pobierania. W przypadku, gdy jest to ważne, naprawdę musisz najpierw zapisać do lokalnego (tymczasowego) pliku, a następnie dostarczyć go, jak pokazano w poprzednim rozdziale.
Metoda użytkowa
Jeśli korzystasz z biblioteki narzędziowej JSF OmniFaces , możesz użyć jednej z trzech wygodnych Faces#sendFile()
metod, przyjmując a File
, lub an InputStream
, lub a byte[]
i określając, czy plik powinien zostać pobrany jako załącznik ( true
) czy inline ( false
).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Tak, ten kod jest kompletny bez zmian. Nie musisz wzywać siebie responseComplete()
i tak dalej. Ta metoda również poprawnie radzi sobie z nagłówkami specyficznymi dla IE i nazwami plików UTF-8. Możesz znaleźć kod źródłowy tutaj .
InputStream
infrastrukturyp:fileDownload
, a nie udało mi się przekonwertowaćOutputStream
naInputStream
. Teraz jest jasne, że nawet detektor akcji może zmienić typ zawartości odpowiedzi, a wtedy odpowiedź i tak zostanie uwzględniona jako plik do pobrania po stronie klienta użytkownika. Dziękuję Ci!preRenderView
detektora w widoku bez znaczników. Odpowiedzi na podobne pytanie dotyczące pobierania (dobrze, serwowania) JSON jest tutaj: stackoverflow.com/questions/8358006/ ...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(); }
źródło
Oto, co zadziałało dla mnie:
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(); }
źródło
tutaj jest pełny fragment kodu 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(); } }
Możesz zmienić logikę odczytu plików, jeśli chcesz, aby plik był generowany w czasie wykonywania.
źródło