Экспорт в Excel JSF и PrimeFaces

#java #jsf-2 #primefaces #apache-poi #tomcat7

#java #jsf-2 #primefaces #apache-poi #tomcat7

Вопрос:

С использованием JDK 1.6, JSF 2.1, PrimeFaces 2.2.1, POI 3.2 и Apache Tomcat 7

Я пытаюсь настроить сервлет, чтобы разрешить загрузку файла Excel на основе пользовательского выбора. Документ Excel создается во время выполнения.

Ошибок нет, и код действительно попадает в сервлет.

Я нажимаю кнопку, и ничего не происходит. Я не использую datatable export, который использует PrimeFaces, потому что мне нужно изменить порядок и пользовательское форматирование данных в документе Excel.

ExportExcelReports.java

 protected void processRequest(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {       
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-Disposition", "attachment; filename="my.xls"");                

    HSSFWorkbook workbook = new HSSFWorkbook();

    HSSFSheet sheet = workbook.createSheet();
    HSSFRow row = sheet.createRow(0);
    HSSFCell cell = row.createCell(0);
    cell.setCellValue(0.0);

    FileOutputStream out = new FileOutputStream("my.xls");
    workbook.write(out);
    out.close();
}
  

ProjectReportBean.java

 public void getReportData() {
    try {
        FacesContext ctx = FacesContext.getCurrentInstance();
        ExternalContext ectx = ctx.getExternalContext();
        HttpServletRequest request = (HttpServletRequest) ectx.getRequest();
        HttpServletResponse response = (HttpServletResponse) ectx.getResponse();
        RequestDispatcher dispatcher = request.getRequestDispatcher("/ExportExcelReports");
        dispatcher.forward(request, response);
        ctx.responseComplete();
    } catch (Exception e) {}
}
  

index.xhtml

 <h:form id="reportsForm">
    <h:outputLabel for="report" value="Reports" /><br />
    <h:selectOneMenu id="report" value="#{projectReportBean.selectedReport}" required="true" requiredMessage="Select Report">
        <f:selectItem itemLabel="---" noSelectionOption="true" />
        <f:selectItems value="#{projectReportBean.reports}" />
    </h:selectOneMenu>

    <p:commandButton action="#{projectReportBean.getReportData}" value="Export" update="revgrid" />                      
</h:form>
  

Ответ №1:

Есть две проблемы.

Первая проблема заключается в том, что <p:commandButton> по умолчанию отправляет Ajax-запрос. Этот запрос запускается кодом JavaScript. Однако JavaScript ничего не может сделать с ответом, который содержит загрузку файла. Из-за ограничений безопасности JavaScript не может создать диалоговое окно Сохранить как или что-то в этомроде. Ответ в основном полностью игнорируется.

Вам нужно добавить ajax="false" в <p:commandButton> , чтобы отключить ajax, чтобы кнопка запускала обычный синхронный HTTP-запрос, или вам нужно заменить его стандартным <h:commandButton> .

 <p:commandButton ajax="false" ... />
  

или

 <h:commandButton ... />
  

Вторая проблема заключается в том, что ваш сервлет вообще не записывает файл Excel в ответ, а вместо этого в локальный файл, который хранится в рабочем каталоге сервера. По сути, HTTP-ответ ничего не содержит. Вам нужно перейти HttpServletResponse#getOutputStream() к WorkBook#write() методу.

 workbook.write(response.getOutputStream());
  

Кстати, мне интересно, насколько сервлет полезен здесь. Вы хотите повторно использовать его за пределами JSF? Если нет, вам вообще не обязательно отправлять на сервлет, а просто выполнить тот же код в методе action компонента. Этот пустой catch блок тоже не очень хорош. Я бы просто объявил это как throws в методе или, по крайней мере, перенастроил его как new FacesException(e) .


Обновление согласно комментариям, вы, похоже, вообще не заинтересованы в сервлете. Вот небольшая правка того, как вы могли бы программно отправить файл Excel в методе действия JSF.

 public void getReportData() throws IOException {
    HSSFWorkbook workbook = new HSSFWorkbook();
    HSSFSheet sheet = workbook.createSheet();
    HSSFRow row = sheet.createRow(0);
    HSSFCell cell = row.createCell(0);
    cell.setCellValue(0.0);

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.setResponseContentType("application/vnd.ms-excel");
    externalContext.setResponseHeader("Content-Disposition", "attachment; filename="my.xls"");

    workbook.write(externalContext.getResponseOutputStream());
    facesContext.responseComplete();
}
  

Комментарии:

1. я не хочу использовать сервлет. думал, что у меня тоже. демонстративно рассмотрите возможность изменения этого после того, как я заработаю.

2.В этом случае проверьте обновление ответа на наличие предложения по перезаписи. ExternalContext Имеет множество методов делегирования. Используйте их. download.oracle.com/javaee/6/api/javax/faces/context/… В конечном итоге вы хотите получить нулевой javax.servlet импорт в вашем коде JSF.

3. просто заставил его работать без сервлета. действительно ценю помощь.

4. @BalusC, должен ли я закрывать ресурс в конце?

Ответ №2:

Вот то, что я написал ранее, и рабочий пример ;

xhtml ;

 <h:panelGrid id="viewCommand" style="float:right;" >
                        <p:commandButton value="Export Excel" icon="ui-icon-document"
                            ajax="false" actionListener="#{xxx.export2Excel}"
                            rendered="#{xxx.showTable}">
                            <p:fileDownload value="#{xxx.exportFile}"
                                contentDisposition="attachment" />
                        </p:commandButton></h:panelGrid>
  

Сторона Java (с POI) ;

 protected void lOBExport2Excel(List table) throws Throwable {
    Row row = null;
    Cell cell = null;
    try {

        Workbook wb = new HSSFWorkbook();
        HSSFCellStyle styleHeader = (HSSFCellStyle) wb.createCellStyle();
        HSSFFont fontHeader = (HSSFFont) wb.createFont();
        fontHeader.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        styleHeader.setFont(fontHeader);
        Sheet sheet = wb.createSheet("sheet");
        row = sheet.createRow((short) 0);

        for (int i = 0; i < columnNames.size(); i  ) {
            cell = row.createCell(i);
            cell.setCellValue(columnNames.get(i));
            cell.setCellStyle(styleHeader);
        }

        int j = 1;

        for (DBData[] temp : tabularData) {
            row = sheet.createRow((short) j);
            for (int k = 0; k < temp.length; k  ) {
                HSSFCellStyle styleRow = (HSSFCellStyle) wb.createCellStyle();
                HSSFFont fontRow = (HSSFFont) wb.createFont();
                fontRow.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
                styleRow.setFont(fontRow);
                cell = row.createCell(k);
                setStyleFormat(temp[k].getDataType(), styleRow, wb);
                cell.setCellValue(temp[k].toFullString());
                cell.setCellStyle(styleRow);
            }

            j  ;
        }

        String excelFileName = getFileName("xls");

        FileOutputStream fos = new FileOutputStream(excelFileName);
        wb.write(fos);
        fos.flush();
        fos.close();

        InputStream stream = new BufferedInputStream(new FileInputStream(excelFileName));
        exportFile = new DefaultStreamedContent(stream, "application/xls", excelFileName);


    } catch (Exception e) {
        catchError(e);
    }

}
  

Комментарии:

1. @spt — это уже рабочие данные, которые добавлены с темой «Сторона Java (с POI)»

Ответ №3:

Я бы также рекомендовал посмотреть на использование загрузки файла PrimeFaces. В зависимости от вашей структуры это может сделать все намного проще. Вам не пришлось бы создавать сервлет, просто управляемый компонент, который может предоставить ContentStream .

Поскольку у вас уже есть написанный сервлет, нет смысла что-то менять, просто пища для размышлений.