JSF बैकिंग बीन से फ़ाइल डाउनलोड कैसे प्रदान करें?


91

क्या जेएसएफ बैकिंग बीन एक्शन विधि से फ़ाइल डाउनलोड प्रदान करने का कोई तरीका है? मैंने बहुत सी चीजों की कोशिश की है। मुख्य समस्या यह है कि मैं OutputStreamफ़ाइल सामग्री को लिखने के लिए प्रतिक्रिया का पता कैसे लगा सकता हूं । मुझे पता है कि इसे कैसे करना है Servlet, लेकिन इसे जेएसएफ फॉर्म से लागू नहीं किया जा सकता है और इसके लिए नए अनुरोध की आवश्यकता होती है।

मुझे OutputStreamवर्तमान से प्रतिक्रिया कैसे मिल सकती है FacesContext?

जवाबों:


238

परिचय

आप सब कुछ प्राप्त कर सकते हैं ExternalContext। JSF 1.x में, आप कच्ची HttpServletResponseवस्तु प्राप्त कर सकते हैं ExternalContext#getResponse()। JSF 2.x में, आप JSF हुड के तहत से ExternalContext#getResponseOutputStream()हड़पने की आवश्यकता के बिना नए प्रतिनिधि विधियों के गुच्छा का उपयोग कर सकते हैं HttpServletResponse

प्रतिक्रिया पर, आपको Content-Typeहेडर सेट करना चाहिए ताकि क्लाइंट को पता चल सके कि कौन सी एप्लिकेशन प्रदान की गई फ़ाइल के साथ संबद्ध है। और, आपको Content-Lengthहेडर सेट करना चाहिए ताकि क्लाइंट डाउनलोड प्रगति की गणना कर सके, अन्यथा यह अज्ञात होगा। और, आपको Content-Dispositionशीर्षलेख सेट करना चाहिए, attachmentयदि आप एक सेव अस डायलॉग चाहते हैं , अन्यथा क्लाइंट इसे इनलाइन प्रदर्शित करने का प्रयास करेगा। अंत में सिर्फ फाइल कंटेंट को रिस्पॉन्स आउटपुट स्ट्रीम में लिखें।

सबसे महत्वपूर्ण हिस्सा FacesContext#responseComplete()जेएसएफ को सूचित करने के लिए कॉल करना है कि फाइल को प्रतिक्रिया में लिखने के बाद उसे नेविगेशन और रेंडरिंग नहीं करना चाहिए, अन्यथा प्रतिक्रिया का अंत पृष्ठ के HTML सामग्री, या पुराने JSF संस्करणों के साथ प्रदूषित हो जाएगा। , आपको IllegalStateExceptionएक संदेश के साथ मिलेगा जैसे getoutputstream() has already been called for this responseकि JSF कार्यान्वयन getWriter()HTML रेंडर करने के लिए कहता है।

Ajax को बंद करें / रिमोट कमांड का उपयोग न करें!

आपको केवल यह सुनिश्चित करने की आवश्यकता है कि कार्रवाई विधि को अजाक्स अनुरोध के द्वारा नहीं कहा जाता है, लेकिन यह कि आपको एक सामान्य अनुरोध के रूप में कहा जाता है, जैसे कि आप आग लगाते हैं <h:commandLink>और <h:commandButton>। अजाक्स अनुरोध और दूरस्थ कमांड को जावास्क्रिप्ट द्वारा नियंत्रित किया जाता है जो बदले में सुरक्षा कारणों से, अजाक्स प्रतिक्रिया की सामग्री के साथ एक सेव अस डायलॉग को बाध्य करने की कोई सुविधा नहीं है ।

यदि आप उदाहरण के लिए PrimeFaces का उपयोग कर रहे हैं <p:commandXxx>, तो आपको यह सुनिश्चित करने की आवश्यकता है कि आप ajax="false"विशेषता के माध्यम से स्पष्ट रूप से ajax को बंद कर दें । यदि आप ICEfaces का उपयोग कर रहे हैं, तो आपको <f:ajax disabled="true" />कमांड कंपोनेंट में घोंसला बनाने की आवश्यकता है ।

जेनेरिक जेएसएफ 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.
}

जेनेरिक जेएसएफ 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);

आम गतिशील फ़ाइल उदाहरण

यदि आपको डायनामिक रूप से जेनरेट की गई फ़ाइल, जैसे कि पीडीएफ या एक्सएलएस, को स्ट्रीम करने की आवश्यकता है, तो बस outputवहीं प्रदान करें जहां एपीआई का उपयोग किया जा रहा है 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();

ध्यान दें कि आप यहां सामग्री की लंबाई निर्धारित नहीं कर सकते। इसलिए आपको प्रतिक्रिया सामग्री की लंबाई निर्धारित करने के लिए लाइन को हटाने की आवश्यकता है। यह तकनीकी रूप से कोई समस्या नहीं है, केवल नुकसान यह है कि एंड्यूसर को एक अज्ञात डाउनलोड प्रगति प्रस्तुत की जाएगी। यदि यह महत्वपूर्ण है, तो आपको वास्तव में पहले एक स्थानीय (अस्थायी) फ़ाइल में लिखना होगा और फिर पिछले अध्याय में दिखाए अनुसार प्रदान करना होगा।

उपयोगिता विधि

आप JSF उपयोगिता पुस्तकालय उपयोग कर रहे हैं OmniFaces , तो आप तीन सुविधाजनक में से एक का उपयोग कर सकते Faces#sendFile()या तो एक लेने के तरीकों File, या एक InputStream, या एक byte[], और निर्दिष्ट करने के लिए कि क्या फ़ाइल एक लगाव (के रूप में डाउनलोड किया जाना चाहिए true) या इनलाइन ( false)।

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

हां, यह कोड पूर्ण है-जैसा है। आपको अपने आप को बुलाने की जरूरत नहीं है responseComplete()। यह विधि आईई-विशिष्ट हेडर और UTF-8 फ़ाइलनाम के साथ भी ठीक से व्यवहार करती है। आप यहां सोर्स कोड पा सकते हैं ।


1
इतना आसान! मैं सोच रहा था कि उनके शोकेस के अनुसार प्राइमफेस के लिए डाउनलोड कैसे उपलब्ध कराया जाए, क्योंकि इसके लिए InputStreamबुनियादी ढाँचे की आवश्यकता होती है p:fileDownload, और मैंने इसे बदलने के तरीके को प्रबंधित नहीं किया OutputStreamहै InputStream। अब यह स्पष्ट है कि एक एक्शन श्रोता भी प्रतिक्रिया सामग्री प्रकार को बदल सकता है और फिर प्रतिक्रिया को वैसे भी उपयोगकर्ता-एजेंट की ओर से एक फ़ाइल डाउनलोड के रूप में सम्मान दिया जाएगा। धन्यवाद!
शवोमिर शैडैरिव

1
क्या HTTP POST (h: कमांडबटन और h: कमांडलिंक) के बजाय HTTP GET का उपयोग करने का कोई तरीका है?
अल्फ्रेडो ओसोरियो

preRenderView@ ऑल्फ्रेडो : हाँ, मार्कअप दृश्य में श्रोता का उपयोग करना । डाउनलोड करने के लिए इसी तरह के सवाल (अच्छी तरह से सेवारत) JSON का उत्तर यहां दिया गया है: stackoverflow.com/questions/8358006/…
BalusC

w3schools.com/media/media_mimeref.asp लिंक टूट गया है। शायद यह एक उपयुक्त है: iana.org/assignments/media-types
जाखड़

2
@BalusC आप हर jsf विषय को कवर करते हैं - मेरे जीवन को आसान बनाने के लिए धन्यवाद सर!
बटलर Xaver

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

}

3

इसी से मेरा काम बना है:

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

1
2 कार्य दिवसों के बाद, इसने मेरी समस्या को थोड़े बदलाव के साथ हल कर दिया :) बहुत बहुत धन्यवाद।
8:MER TAŞCI

@ --MERTAŞCI: क्या बदलता है,
कुकेलजे

-3

यहाँ पूरा कोड स्निपेट है 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();
   }
 }

यदि आप फ़ाइल को रनटाइम पर जनरेट करना चाहते हैं तो आप फ़ाइल रीडिंग लॉजिक को बदल सकते हैं।


यह आपको केवल इनपुट फ़ाइल का हिस्सा मिलेगा, अगर इसकी बड़ी कि 1024 बाइट्स!
hinneLinks 8
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.