当前位置: 首页 > 知识库问答 >
问题:

使用Apache POI生成的Excel已损坏,返回fronm API REST spring boot

龚博涛
2023-03-14

编辑:

如果我直接从浏览器命中endpoint,文件就会被正确下载。所以我猜问题出在前面,以及用接收到的数据创建和保存文件的方式。

我有一个Java/spring boot的应用程序,我想在其中构建一个APIendpoint,它创建并返回一个可下载的excel文件。以下是我的控制器endpoint:

 @GetMapping(path = "/informe/{informeDTO}")
    public ResponseEntity<InputStreamResource> generarInforme(@PathVariable(value = "informeDTO") String informeDTOString) throws JsonParseException, JsonMappingException, IOException {
    
        

 final InformeDTO informeDTO =
                new ObjectMapper().readValue(informeDTOString, InformeDTO.class);
     
    List<InformeDTO> listDatosinformeDTO = utilsService.getDatosInformeDTO(informeDTO);

    for (InformeDTO informeDTO2 : listDatosinformeDTO) {
        logger.debug(informeDTO2);
    }

    ByteArrayInputStream in = createReport(listDatosinformeDTO);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Disposition", "attachment; filename=IOPreport.xlsx");

    return ResponseEntity.ok().headers(headers)
            .contentType(
                    MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
            .body(new InputStreamResource(in));

}

这是角度控制器:

  function generarInformeIOP(){
            
  
            InformesService.generarInformeIOP($scope.informeView.sociedad,  $scope.informeView.area, $scope.informeView.epigrafe,
                     $scope.informeView.cuenta, $scope.informeView.status, $scope.informeView.organizationalUnit, 
                     $scope.informeView.societyGL, $scope.informeView.calculationType, $scope.informeView.provincia, $scope.informeView.financialSegment,
                     $scope.informeView.loadDateFrom, $scope.informeView.loadDateTo,  $scope.informeView.incomeDateFrom,  $scope.informeView.incomeDateTo)
             .then(
                 function(response)
                 {
                     var blob = new Blob([response.data], {
                         type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8"
                     });
                     saveAs(blob, "IOPreport.xlsx");
                     
                      $scope.informeFunctionality.errorMessage = '';
                 },
                 function(errResponse)
                 {
                     console.log("ERROR: " + errResponse.data);
                     $scope.informeFunctionality.errorMessage = "Ha ocurrido un error inesperado: " + errResponse.data.error + 
                         ": " + errResponse.data.message;
                 }
             )
            
        }

和服务:

....
    $http.get(urls.SERVICE_API + "informe/"+ angular.toJson(informeDTO)).then(
                                function(response) {
                                    console.log("GenerarInformeIOP - success");
                                    deferred.resolve(response);
                                }, function(errResponse) {

                                    console.log("GenerarInformeIOP - error");
                                    deferred.reject(errResponse);

                                });
...

生成成功,文件已下载,但我认为它已损坏,因为Excel无法打开它。

有什么不对劲吗?

编辑(添加createReport):

        private ByteArrayInputStream createReport(List<InformeDTO> datosInforme) {
        ByteArrayInputStream result =null;
        
        try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream out = new ByteArrayOutputStream();) {
            Set<String> columns = new LinkedHashSet<String>();

            // Coumnas fijas
            columns.add("Cuenta");
            columns.add("Epigrafe");
            columns.add("Descripcion");
            columns.add("Total_Importe");

            // Columnas dinamicas
            /*
             * Tedremos que recorrer todas las filas puesto que no sabremos si una traera
             * menos periodos que otra de esta manera obtendremos todos los periodos
             */
            for (InformeDTO informeDTO : datosInforme) {
                for (Map.Entry<String, Double> entry : informeDTO.getTotalByPeriodoContable().entrySet()) {
                    columns.add(entry.getKey());
                }
            }

            /*
             * CreationHelper helps us create instances for various things like DataFormat,
             * Hyperlink, RichTextString etc in a format (HSSF, XSSF) independent way
             */
//          CreationHelper createHelper = workbook.getCreationHelper();

            // Create a Sheet
            Sheet sheet = workbook.createSheet("IOPReport");

            // Create a Font for styling header cells
            Font headerFont = workbook.createFont();
            headerFont.setBold(true);
            headerFont.setFontHeightInPoints((short) 14);
            headerFont.setColor(IndexedColors.RED.getIndex());

            // Create a CellStyle with the font
            CellStyle headerCellStyle = workbook.createCellStyle();
            headerCellStyle.setFont(headerFont);

            // Create a Row
            Row headerRow = sheet.createRow(0);

            // Creating cells
            int i = 0;
            for (String value : columns) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(value);
                cell.setCellStyle(headerCellStyle);
                i++;
            }

            // Create Other rows and cells with employees data
            int rowNum = 1;
            int cellDynamicNum = 0;
            for (InformeDTO informeDTO : datosInforme) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(informeDTO.getCuenta());
                row.createCell(1).setCellValue(informeDTO.getEpigrafe());
                row.createCell(2).setCellValue(informeDTO.getDescripcion_epigrafe());
                row.createCell(3).setCellValue("No Data");
                cellDynamicNum = 4;
                for (Map.Entry<String, Double> entry : informeDTO.getTotalByPeriodoContable().entrySet()) {
                    row.createCell(cellDynamicNum).setCellValue(entry.getValue());
                    cellDynamicNum++;
                }

            }

            // Resize all columns to fit the content size
            for (i = 0; i < columns.size(); i++) {
                sheet.autoSizeColumn(i);
            }

            // Write the output to a file

            workbook.write(out);
             result = new ByteArrayInputStream(out.toByteArray());
            out.close();
            workbook.close();

        } catch (Exception e) {
            logger.debug("Excepcion en la creacion del report " + e);
        }
        return result;
    }

问候

共有2个答案

刘成礼
2023-03-14

我不确定为什么使用responseEntity 。我有另一个使用字节数组responseEntity 的工作解决方案。下面附上了一些代码片段:

创建工作簿后,将其写入OutputStream:

private final MediaType mediaType = MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

...
...
// at the end
ByteArrayOutputStream stream = new ByteArrayOutputStream();
workbook.write(stream);
return getDownload(stream.toByteArray(), filename, mediaType);

....

下载方法:

    public static ResponseEntity<byte[]> getDownload(byte[] content, String filename, MediaType mediaType) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentLength(content.length);
        headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
        headers.set(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        headers.setContentType(mediaType);

        return new ResponseEntity<>(content, headers, HttpStatus.OK);
    }

如果能行就告诉我。

艾泰
2023-03-14

在此处生成输出时:

result = new ByteArrayInputStream(out.toByteArray());

工作簿在关闭之前不会保存到out中。因此您需要将顺序更改为:

 workbook.close()
 result = new ByteArrayInputStream(out.toByteArray());

关闭ByteArrayOutputStream并不是必须的,但如果保留它也没关系。

 类似资料:
  • 我正在使用ApachePOI,我创建了一个XSSF工作簿,并尝试打开一个xlsx文件。它在当地的一个地方很有效。但是,当我用Excel打开真正服务器(AWS EC2、Tomcat8、JDK 1.8)上的Excel文件时,它显示文件已损坏(.xls工作)。这是我的代码: 本地Spring4, jdk1.8, tomcat 8.0, maven 真正的AWS EC2亚马逊linux2,jdk1。8、t

  • 我正在使用Cordova构建一个Android应用程序,因为我对Android一无所知。 我执行了以下步骤来创建和运行Cordova应用程序: 安装JDK 8 安装Gradle 安装Android Studio 在Android Studio中使用SDK管理器安装SDK版本28(我的目标是Fire TV设备) (目标API级别28) 这是最后一个命令输出的结尾: 我尝试卸载并重新安装SDK,但我仍

  • 我正在ServletOutputStream上编写一个excel文件(使用HSSFWorkBook for xls和XSSFWorkBook forxlsx)。作为ServletResponse的一部分下载的excel已损坏并包含垃圾字符。 我尝试过将Excel文件写入FileOutpuStream,它工作正常。Excel文件可读且完好无损。 已经检查了关于同一问题的其他几个StackOverfl

  • 当我构建我的android应用程序时,它给出了错误“安装的构建工具版本30.0.3被损坏。删除并使用SDK管理器重新安装”我试图通过更新我的SDK管理器来修复这个问题,但它不起作用。我如何修复这个错误。

  • 我正在使用java中的Apache poi库生成excel报表。 为了生成excel报表,我正在读取使用管道分隔的文本文件,并将其写入excel表单中。 有人对此有什么建议吗。读写时是否有内存问题。文件是逐行读取,逐页写入。

  • Docx4J生成的Excel工作簿总是说损坏了,但我无法确定Excel不喜欢底层XML的什么,更不用说如何修复它了。 我的用例如下:我试图定期自动生成一个带有图表和图形的excel工作簿。只有原始数据会改变,但随着原始数据的改变,其他一切都会动态更新。 null null 在我的空白工作簿之前和之后 欢迎所有的想法。