[英]Apache POI 3.14 Writing SXSSF to ServletResponse OutputStream is corrupting workbook JSF
在我的Web應用程序中,我正在構建SXSSFWorkbook對象,以生成每個包含大約30-60k記錄的報告。 我通過請求首先啟動並構建每個SXSSFWorkbook的過程啟動了一個過程。 我能夠生成報告並打開FileOutputStream以將對象導出到桌面(當然是本地)。 但是,我想讓用戶選擇通過請求(JSF)將哪個報告下載到服務器。 當我從servlet響應中饋送OutputStream時,我可以下載.xlsx文件,但它告訴我它已損壞。 我已經進行了一些研究,嘗試了一些不同的解決方法,但都沒有結果。 發表是我的代碼。 我有點不知所措。
ps我以前曾生成HSSFWorkbook對象並下載它們,但是它們開始引起堆空間問題。 因此,切換到SXSSFWorkbook。
我的命令按鈕
<h:commandButton value="Download Report" styleClass="secondary-button"
action="#{backingBean.getLatestReport}"
id="thirdReportButton">
</h:commandButton>
我的行動
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
最近,我成功地完成了類似的任務,因此也許可以為您提供幫助。
我剛剛運行了您的代碼(在使用Chrome作為瀏覽器的Payara 4.1上)添加了您在帖子中省略的部分
@ManagedBean(name = "backingBean")
@ViewScoped
public class BackingBean {
//your command button should call getLatestReport and not getSecondReport() as in your original post
public void getLatestReport() throws Exception {
FacesContext faces = FacesContext.getCurrentInstance();
String templateName = "Report.xlsx";
HttpServletResponse response = null;
OutputStream outputStream = null;
//workbookForLatestReport is a SXSSFWorkbook object
//MY ADDITION START
//I've created SXSSFWorkbook object since your post did't contain this part
//100K rows, 100 columns
SXSSFWorkbook workbookForLatestReport = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE);
workbookForLatestReport.setCompressTempFiles(true);
SXSSFSheet sheet = workbookForLatestReport.createSheet();
for (int rowNumber = 0; rowNumber < 100000; rowNumber++) {
SXSSFRow row = sheet.createRow(rowNumber);
for (int columnNumber = 0; columnNumber < 100; columnNumber++) {
SXSSFCell cell = row.createCell(columnNumber);
cell.setCellValue("ROW " + rowNumber + " COLUMN " + columnNumber);
}
}
//MY ADDITION END
try {
if (workbookForLatestReport != null) {
response = (HttpServletResponse) faces.getExternalContext()
.getResponse();
response.reset();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition",
"attachment; filename=\"" + templateName + "\"");
outputStream = response.getOutputStream();
workbookForLatestReport.write(outputStream);
outputStream.close();
workbookForLatestReport.dispose();
}
faces.renderResponse();
} catch (Exception e) {
throw e;
}
}
}
它工作正常且符合預期。
我有2條建議:
action="#{backingBean.getSecondReport}"
但是您的托管bean操作被命名為public void getLatestReport
。 檢查它是否鍵入錯誤。 我可以借助省略號和更改一些代碼來找到解決方案。
創建工作簿后,我使用ByteArrayStream在我的模型bean上設置了byte []屬性,當單擊來自jsf的下載偵聽器時可以引用該屬性。
ByteArrayOutputStream bos;
byte[] workbookForLatestBytes;
XSSFXWorkbook workbookForLatestReport;
.
.
.
workbookForLatestReport = <generate the report here>
if(workbookForLatestReport != null){
bos = new ByteArrayOutputStream();
workbookForLatestReport.write(bos);
workbookForLatestBytes = bos.toByteArray();
workbookForPreviousReport.dispose();
}
這是我的JSF代碼中觸發的操作。
<h:commandButton value="Download Report"
styleClass="secondary-button"
action="#{productRuleAuditCompareBackingBean.getSecondReport}"
id="thirdReportButton"
rendered="#{not empty productRuleAuditCompareModelBean.workbookForLatestBytes}">
</h:commandButton>
我的后備bean操作如下。 在將字節數組寫入響應輸出流之前,我重置了HTTP響應。
public void getSecondReport() {
FacesContext faces = FacesContext.getCurrentInstance();
try {
faces.getExternalContext().responseReset();
this.prAuditCompareModelBean.getLatestReport();
faces.responseComplete();
} catch (Exception e) {
PODBException.wrap(e, PODBExceptionInformation.create(
PODBExceptionType.ERROR,
ProductRuleAuditCompareBackingBean.class.getName(),
"getting first report",
"Error while getting second report for download", "1"));
}
}
在這里,我使用Omniface Faces#sendFile方法將工作簿寫入響應輸出流。
public void getLatestReport() throws Exception {
try {
if (getWorkbookForLatestBytes() != null) {
Faces.sendFile(getWorkbookForLatestBytes(), reportName, true);
}
} catch (Exception e) {
throw e;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.