簡體   English   中英

Apache POI 3.14將SXSSF寫入ServletResponse OutputStream破壞了工作簿JSF

[英]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 檢查它是否鍵入錯誤。
  • 將您的SXSSFWorkbook對象創建代碼與我的示例進行比較。 有什么重要的區別嗎?

我可以借助省略號和更改一些代碼來找到解決方案。

創建工作簿后,我使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM