บทนำ
คุณสามารถผ่านทุกอย่าง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 / อย่าใช้คำสั่งระยะไกล!
คุณจะต้องตรวจสอบให้แน่ใจว่าวิธีการดำเนินการไม่เรียกตามคำขออาแจ็กซ์ แต่มันถูกเรียกโดยการร้องขอปกติเมื่อคุณยิงด้วยและ<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();
โปรดทราบว่าคุณไม่สามารถกำหนดความยาวของเนื้อหาได้ที่นี่ ดังนั้นคุณต้องลบบรรทัดเพื่อตั้งค่าความยาวของเนื้อหาตอบกลับ นี่ไม่ใช่ปัญหาในทางเทคนิคข้อเสียเพียงอย่างเดียวคือ enduser จะแสดงความคืบหน้าในการดาวน์โหลดที่ไม่รู้จัก ในกรณีนี้มีความสำคัญคุณต้องเขียนลงในไฟล์ภายใน (ชั่วคราว) ก่อนจากนั้นจึงจัดเตรียมตามที่แสดงในบทก่อน
วิธียูทิลิตี้
หากคุณใช้ไลบรารียูทิลิตี้ 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
ไฟล์. ตอนนี้เป็นที่ชัดเจนว่าแม้แต่ผู้ฟังการดำเนินการก็สามารถเปลี่ยนประเภทเนื้อหาการตอบกลับได้จากนั้นการตอบกลับจะได้รับการยอมรับเป็นการดาวน์โหลดไฟล์ที่ฝั่งตัวแทนผู้ใช้ ขอขอบคุณ!