Запись данных в шаблон Excel с множеством формул, стилей и цветов занимает слишком много времени - PullRequest
0 голосов
/ 05 апреля 2019

Я пытаюсь записать данные в шаблон Excel, который содержит много формул и стилей. Если я скопировал формулу / форматирование в 100 строк * 601 ячеек, Excel загрузится через секунду после записи данных, но как только я увеличу его до 3000 строк * 601 ячеек, и даже если я запишу его в 70 строк, это займет вечность и зависает сервер. Это происходит потому, что workbook.write (outputtream) занимает слишком много времени. Если я увеличу размер кучи, он будет работать с 1500 строками, загрузка которого занимает от 15 до 20 секунд.

Я пробовал SXSSFWorkbook, но это не позволяет мне писать в существующих строках. Чтобы избежать этого, я держу одну XSSFWorkbook для чтения данных и SXSSFWorkbook для записи, проблема в том, что я не смог получить цвета и стиль из моего фактического шаблона в другой, и данные также испортились.

Это работает, но я должен увеличить размер кучи с увеличением количества строк, я проверил его на 1500 строк, результирующий размер файла составляет 3,5 МБ.

private Workbook dumpDataToMaster(List<Map<String, Object>> excelData,Map<String, String> fileType) throws IOException {
        Workbook workbook = new XSSFWorkbook(this.getClass().getResourceAsStream("/excel_templates/"+fileType.get("file")));

        Sheet sheet = workbook.getSheet(fileType.get("sheetName"));
        Iterator<Cell> headerItr = sheet.getRow(3).iterator();
        List<String> headers = new ArrayList<>();



        while(headerItr.hasNext()) {
            headers.add(headerItr.next().getStringCellValue());
        }

        int headersSize = headers.size();
        int excelDataSize = excelData.size();

        IntStream.range(0,excelDataSize).forEach(eIndex->{
            Row dataRow = sheet.getRow(eIndex+4);
            IntStream.range(0,headersSize).forEach(index->{

                Object value = excelData.get(eIndex).get(headers.get(index));
                if(value != null) {
                    if (value instanceof String) {
                        dataRow.getCell(index).setCellValue((String)value);
                    } else if(value instanceof Number) {
                        dataRow.getCell(index).setCellValue(((Number)value).doubleValue());
                    } else if(value instanceof Date) {
                        dataRow.getCell(index).setCellValue((Date)value);
                    }
                }
            });
        });


        sheet.getRow(1).getCell(1).setCellValue(Calendar.getInstance().getTime());
        workbook.setForceFormulaRecalculation(true);
        return workbook;
    }

Контроллер для вышеуказанного метода выглядит так

public void downloadDDTemplate(@RequestParam String values, @OrgModelAttribute("sessionElements") SessionElements sessionElements, HttpServletResponse response) throws IOException {
        OrderTemplateType orderTemplateType = objectMapper.readValue(values, OrderTemplateType.class);
        response.setHeader(Constant.CONTENT_DISPOSITION,
                "attachment; filename=\"" +  orderTemplateType.getDisplay()+" - Template - "
                        + DateTimeFormatConfiguration.getFileNameDateFormat().format(Calendar.getInstance().getTime())
                        + ".xlsx\"");
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        orderMasterService.downloadDDTemplate(orderTemplateType).write(response.getOutputStream());
        response.flushBuffer();
    }

второй метод, который я попробовал, это

private Workbook dumpDataToMaster(List<Map<String, Object>> excelData,Map<String, String> fileType, HttpServletResponse response) throws IOException, InvalidFormatException {
        Workbook workbook = new XSSFWorkbook(this.getClass().getResourceAsStream("/excel_templates/"+fileType.get("file")));

        XSSFWorkbook readWb = new XSSFWorkbook(workbook);
        Sheet readSheet = readWb.getSheet(fileType.get("sheetName"));
        //inputStream.close();

        SXSSFWorkbook writeWb = new SXSSFWorkbook(); 
        writeWb.setCompressTempFiles(true);
        SXSSFSheet writeSheet = writeWb.createSheet(fileType.get("sheetName"));//(SXSSFSheet) wb.getSheet(fileType.get("sheetName"));
        writeSheet.setRandomAccessWindowSize(1600);
        Iterator<Cell> headerItr = readSheet.getRow(3).iterator();
        List<String> headers = new ArrayList<>();

        CellStyle dateStyle = writeWb.createCellStyle();
        CreationHelper createHelper = writeWb.getCreationHelper();
        dateStyle.setDataFormat(
            createHelper.createDataFormat().getFormat("dd-mm-yyyy"));


        while(headerItr.hasNext()) {
            String value = headerItr.next().getStringCellValue();
            headers.add(value);
            Row auxWriteRow = writeSheet.createRow(3);
            Cell auxWriteCell = auxWriteRow.createCell(headers.size()-1);
            auxWriteCell.setCellValue(value);
        }

        int headersSize = headers.size();
        int excelDataSize = excelData.size();

        IntStream.range(0,excelDataSize).forEach(eIndex->{
            Row dataRow = readSheet.getRow(eIndex+4);
            Row writeRow = writeSheet.createRow(eIndex+4);
            IntStream.range(0,headersSize).forEach(index->{

                Cell writeCell = writeRow.createCell(index);
                Cell readCell = dataRow.getCell(index);
                Object value = excelData.get(eIndex).get(headers.get(index));
                if(readCell != null && CellType.FORMULA.equals(readCell.getCellType())) {
                    writeCell.setCellFormula(readCell.getCellFormula());
                }
                writeCell.getCellStyle();
                if(value != null) {
                    if (value instanceof String) {
                        XSSFCellStyle newCellStyle = (XSSFCellStyle) writeCell.getSheet().getWorkbook().createCellStyle();     
                        writeCell.setCellStyle(copyCellStyle((XSSFCell)readCell, newCellStyle)); 
                        writeCell.setCellType(readCell.getCellType());
                        writeCell.setCellValue((String)value);
                    } else if(value instanceof Number) {
                        XSSFCellStyle newCellStyle = (XSSFCellStyle) writeCell.getSheet().getWorkbook().createCellStyle();     
                        writeCell.setCellStyle(copyCellStyle((XSSFCell)readCell, newCellStyle)); 
                        writeCell.setCellStyle(readCell.getCellStyle()); 
                        writeCell.setCellType(readCell.getCellType());
                        writeCell.setCellValue(((Number)value).doubleValue());
                    } else if(value instanceof Date) {
                        writeCell.setCellStyle(dateStyle); 
                        writeCell.setCellType(readCell.getCellType());
                        writeCell.setCellValue((Date)value);
                    }
                }
            });
        });

        int lastRow = sheet.getLastRowNum();

        for (int eIndex=0; eIndex<=lastRow; eIndex++) {
            Row dataRow = sheet.getRow(eIndex+excelDataSize+4);
            if(dataRow == null)
                break;
            sheet.removeRow(dataRow);
        }
        Cell cell = writeSheet.createRow(1).createCell(1);
        cell.setCellStyle(dateStyle);
        cell.setCellValue(Calendar.getInstance().getTime());
        XSSFFormulaEvaluator.evaluateAllFormulaCells(workbook);
        writeWb.setForceFormulaRecalculation(true);
        writeWb.write(response.getOutputStream());
        writeWb.close();
        readWb.close();
        return writeWb;
    }

Есть ли способ добиться этого быстрее, с меньшим объемом памяти при потреблении.

...