Giriş
Her şeyi halledebilirsiniz ExternalContext. JSF 1.x'te ham HttpServletResponsenesneyi ExternalContext#getResponse(). JSF 2.x'de, JSF davlumbazlarının altından ExternalContext#getResponseOutputStream()kapmaya gerek kalmadan bir dizi yeni delege yöntemini kullanabilirsiniz HttpServletResponse.
Yanıtta, Content-Typeistemcinin sağlanan dosyayla hangi uygulamayı ilişkilendireceğini bilmesi için üstbilgiyi ayarlamalısınız . Content-LengthÜstbilgiyi istemcinin indirme ilerlemesini hesaplayabileceği şekilde ayarlamalısınız , aksi takdirde bilinmeyecektir. Ve, Farklı Kaydet iletişim kutusu istiyorsanız Content-Dispositionbaşlığı olarak ayarlamalısınız , aksi takdirde istemci bunu satır içi olarak görüntülemeye çalışacaktır. Son olarak, dosya içeriğini yanıt çıktı akışına yazın.attachment
En önemli kısım, FacesContext#responseComplete()dosyayı yanıta yazdıktan sonra gezinme ve işleme gerçekleştirmemesi gerektiğini JSF'ye bildirmektir, aksi takdirde yanıtın sonu sayfanın HTML içeriğiyle veya eski JSF sürümlerinde kirlenir. , bir alacak IllegalStateExceptiongibi bir mesaj ile getoutputstream() has already been called for this responseJSF uygulama çağrıları yaparken getWriter()HTML işlemek için.
Ajax'ı kapatın / uzaktan kumanda kullanmayın!
Yalnızca eylem yöntemi olduğundan emin olmak gerekir değil bir ajax isteği tarafından çağrılan, ancak birlikte ateş gibi normal bir istek tarafından çağrılmasını <h:commandLink>ve <h:commandButton>. Ajax istekleri ve uzak komutlar JavaScript tarafından işlenir ve bu da güvenlik nedenleriyle ajax yanıtının içeriğiyle Farklı Kaydet diyaloğunu zorlayacak hiçbir tesise sahip değildir.
Örneğin PrimeFaces kullanıyorsanız <p:commandXxx>, ajax="false"öznitelik aracılığıyla ajax'ı açıkça kapattığınızdan emin olmanız gerekir . ICEfaces kullanıyorsanız <f:ajax disabled="true" />, komut bileşenine bir yerleştirmeniz gerekir .
Genel JSF 2.x örneği
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();
}
Genel JSF 1.x örneği
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();
}
Yaygın statik dosya örneği
Yerel disk dosya sisteminden statik bir dosya yayınlamanız gerekirse, kodu aşağıdaki gibi değiştirin:
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);
Ortak dinamik dosya örneği
PDF veya XLS gibi dinamik olarak oluşturulmuş bir dosyayı yayınlamanız outputgerekirse, kullanılan API'nin bir OutputStream.
Örneğin iText PDF:
String fileName = "dynamic.pdf";
String contentType = "application/pdf";
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
document.close();
Örneğin Apache POI HSSF:
String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";
HSSFWorkbook workbook = new HSSFWorkbook();
workbook.write(output);
workbook.close();
Burada içerik uzunluğunu ayarlayamayacağınızı unutmayın. Bu nedenle, yanıt içeriği uzunluğunu ayarlamak için satırı kaldırmanız gerekir. Bu teknik olarak sorun değil, tek dezavantajı, son kullanıcıya bilinmeyen bir indirme ilerlemesi sunulacak olmasıdır. Bunun önemli olması durumunda, gerçekten önce yerel (geçici) bir dosyaya yazmanız ve ardından önceki bölümde gösterildiği gibi sağlamanız gerekir.
Fayda yöntemi
JSF yardımcı program kitaplığı OmniFaces kullanıyorsanız , Faces#sendFile()a File, veya bir InputStreamveya a alarak byte[]ve dosyanın ek ( true) veya satır içi ( false) olarak indirilip indirilmeyeceğini belirleyen üç uygun yöntemden birini kullanabilirsiniz .
public void download() throws IOException {
Faces.sendFile(file, true);
}
Evet, bu kod olduğu gibi tamamlandı. responseComplete()Kendinizi çağırmanıza gerek yok . Bu yöntem aynı zamanda IE'ye özgü üstbilgiler ve UTF-8 dosya adlarıyla da düzgün bir şekilde ilgilenir. Kaynak kodunu burada bulabilirsiniz .
InputStreamaltyapısınıp:fileDownloadve ben dönüştürmek nasıl yönetilen değilOutputStreametmekInputStream. Artık bir eylem dinleyicisinin bile yanıt içerik türünü değiştirebileceği ve ardından yanıt, kullanıcı aracısı tarafında bir dosya indirmesi olarak kabul edileceği açıktır. Teşekkür ederim!