pengantar
Anda bisa menyelesaikan semuanya ExternalContext
. Di JSF 1.x, Anda bisa mendapatkan HttpServletResponse
objek mentah dengan ExternalContext#getResponse()
. Di JSF 2.x, Anda dapat menggunakan sekumpulan metode delegasi baru seperti ExternalContext#getResponseOutputStream()
tanpa perlu mengambil HttpServletResponse
dari bawah tenda JSF.
Pada tanggapannya, Anda harus mengatur Content-Type
tajuk sehingga klien mengetahui aplikasi mana yang akan dikaitkan dengan file yang disediakan. Dan, Anda harus mengatur Content-Length
tajuk sehingga klien dapat menghitung kemajuan unduhan, jika tidak maka tidak akan diketahui. Dan, Anda harus menyetel Content-Disposition
tajuk ke attachment
jika Anda menginginkan dialog Simpan Sebagai , jika tidak, klien akan mencoba menampilkannya sebaris. Terakhir, cukup tulis konten file ke aliran keluaran respons.
Bagian terpenting adalah memanggil FacesContext#responseComplete()
untuk memberi tahu JSF bahwa JSF tidak boleh melakukan navigasi dan rendering setelah Anda menulis file ke respons, jika tidak, akhir respons akan tercemar dengan konten HTML halaman, atau dalam versi JSF yang lebih lama , Anda akan mendapatkan IllegalStateException
pesan seperti getoutputstream() has already been called for this response
saat implementasi JSF memanggil getWriter()
untuk merender HTML.
Matikan ajax / jangan gunakan perintah jarak jauh!
Anda hanya perlu memastikan bahwa metode aksi tidak dipanggil oleh permintaan ajax, tetapi dipanggil oleh permintaan normal saat Anda mengaktifkan <h:commandLink>
dan <h:commandButton>
. Permintaan Ajax dan perintah jarak jauh ditangani oleh JavaScript yang pada gilirannya, karena alasan keamanan, tidak memiliki fasilitas untuk memaksa dialog Simpan Sebagai dengan konten respons ajax.
Jika Anda menggunakan misalnya PrimeFaces <p:commandXxx>
, maka Anda perlu memastikan bahwa Anda secara eksplisit menonaktifkan ajax via ajax="false"
atribut. Jika Anda menggunakan ICEfaces, maka Anda perlu menumpuk <f:ajax disabled="true" />
di komponen perintah.
Contoh JSF 2.x generik
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();
}
Contoh JSF 1.x generik
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();
}
Contoh file statis umum
Jika Anda perlu mengalirkan file statis dari sistem file disk lokal, gantikan kode seperti di bawah ini:
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);
Contoh file dinamis umum
Jika Anda perlu melakukan streaming file yang dibuat secara dinamis, seperti PDF atau XLS, cukup sediakan di output
sana di mana API yang digunakan mengharapkan OutputStream
.
Misalnya iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
document.close();
Misalnya Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.write(output);
workbook.close();
Perhatikan bahwa Anda tidak dapat mengatur panjang konten di sini. Jadi, Anda perlu menghapus garis untuk menyetel panjang konten respons. Secara teknis ini tidak masalah, satu-satunya kelemahan adalah bahwa pengguna akhir akan disajikan kemajuan unduhan yang tidak diketahui. Jika ini penting, maka Anda benar-benar perlu menulis ke file lokal (sementara) terlebih dahulu dan kemudian menyediakannya seperti yang ditunjukkan pada bab sebelumnya.
Metode utilitas
Jika Anda menggunakan pustaka utilitas JSF OmniFaces , Anda dapat menggunakan salah satu dari tiga Faces#sendFile()
metode praktis yang menggunakan a File
, atau an InputStream
, atau a byte[]
, dan menentukan apakah file harus diunduh sebagai attachment ( true
) atau inline ( false
).
public void download() throws IOException {
Faces.sendFile(file, true);
}
Ya, kode ini lengkap apa adanya. Anda tidak perlu memohon responseComplete()
dan seterusnya sendiri. Metode ini juga menangani dengan tepat header khusus IE dan nama file UTF-8. Anda dapat menemukan kode sumber di sini .
InputStream
infrastruktur untukp:fileDownload
, dan saya belum berhasil mengonversiOutputStream
keInputStream
. Sekarang jelas bahwa bahkan pendengar tindakan dapat mengubah jenis konten tanggapan dan kemudian tanggapan tersebut akan dianggap sebagai unduhan file di sisi agen pengguna. Terima kasih!