Pregunta

Usando JDK 1.6, JSF 2.1, PrimeFaces 2.2.1, POI 3.2 y Apache Tomcat 7

Estoy tratando de configurar un servlet para permitir una descarga de un archivo de Excel basado en la selección del usuario. El documento de Excel se crea en tiempo de ejecución.

Sin errores y el código entra en el servlet.

Hago clic en el botón y no pasa nada. No estoy utilizando la exportación DataTable que usa PrimeFaces porque necesito hacer reordenamiento y formateo personalizado en los datos en el documento de 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();
}

Projecteportbean.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>
¿Fue útil?

Solución

Hay dos problemas.

El primer problema es que el <p:commandButton> Envía por defecto una solicitud AJAX. Esta solicitud es despedida por el código JavaScript. Sin embargo, JavaScript no puede hacer nada con una respuesta que contenga una descarga de archivo. Debido a restricciones de seguridad, JavaScript no puede generar un Guardar como diálogo o algo. La respuesta es básicamente ignorada totalmente.

Necesitas agregar ajax="false" a <p:commandButton> Para apagar AJAX de modo que el botón dispare una solicitud HTTP sincrónica normal, o necesite reemplazarlo por estándar <h:commandButton>.

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

o

<h:commandButton ... />

El segundo problema es que su servlet no escribe el archivo de Excel a la respuesta, sino en un archivo local que se almacena en el directorio de trabajo del servidor. Básicamente, la respuesta HTTP contiene nada. Necesitas pasar HttpServletResponse#getOutputStream() hacia WorkBook#write() método.

workbook.write(response.getOutputStream());

En una nota no relacionada, me pregunto cómo el servlet es útil aquí. ¿Quieres reutilizarlo fuera de JSF? Si no, no necesariamente necesita enviar al servlet en absoluto, sino simplemente ejecutar el mismo código en el método de acción de Bean. Que vacío catch El bloque tampoco es agradable. Solo lo declararía como throws en el método o al menos volver a revelarlo como new FacesException(e).


Actualizar Según los comentarios, parece que no está interesado en el servlet en absoluto. Aquí hay una reescritura menor de cómo podría enviar el archivo de Excel programáticamente en un método de acción 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();
}

Otros consejos

Aquí está el que escribí antes y el caso de trabajo;

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>

Lado de Java (con 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);
    }

}

También recomendaría mirar el uso de PrimeFaces FilDeoWownload. Dependiendo de su estructura, podría hacer que todo esto sea mucho más fácil. No tendría que crear un servlet solo un frijol administrado que pueda proporcionar un ContentStream.

Sin embargo, dado que ya tiene el servlet escrito, no tiene sentido cambiar, solo comida para pensar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top