diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java b/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java index af7bcecf..60654fe5 100644 --- a/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java +++ b/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java @@ -20,7 +20,7 @@ public @interface ContentLoopMerge { * * @return */ - int eachRow() default -1; + int eachRow() default 1; /** * Extend column diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index af90d8d5..6779e875 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -200,6 +200,9 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } Set dataKeySet = new HashSet<>(dataMap.keySet()); + RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(writeContext, + null, relativeRowIndex, Boolean.FALSE); + for (AnalysisCell analysisCell : analysisCellList) { CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( writeContext, null, analysisCell.getRowIndex(), null, analysisCell.getColumnIndex(), @@ -215,7 +218,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable); cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); - createCell(analysisCell, fillConfig, cellWriteHandlerContext); + createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); cellWriteHandlerContext.setOriginalValue(value); cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); @@ -236,7 +239,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { cellWriteHandlerContext.setExcelContentProperty(ExcelContentProperty.EMPTY); cellWriteHandlerContext.setIgnoreFillStyle(Boolean.TRUE); - createCell(analysisCell, fillConfig, cellWriteHandlerContext); + createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); Cell cell = cellWriteHandlerContext.getCell(); for (String variable : analysisCell.getVariableList()) { @@ -288,6 +291,11 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); } + + // In the case of the fill line may be called many times + if (rowWriteHandlerContext.getRow() != null) { + WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); + } } private Integer getRelativeRowIndex() { @@ -302,13 +310,16 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } private void createCell(AnalysisCell analysisCell, FillConfig fillConfig, - CellWriteHandlerContext cellWriteHandlerContext) { + CellWriteHandlerContext cellWriteHandlerContext, RowWriteHandlerContext rowWriteHandlerContext) { Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { Row row = cachedSheet.getRow(analysisCell.getRowIndex()); cellWriteHandlerContext.setRow(row); Cell cell = row.getCell(analysisCell.getColumnIndex()); cellWriteHandlerContext.setCell(cell); + rowWriteHandlerContext.setRow(row); + rowWriteHandlerContext.setRowIndex(analysisCell.getRowIndex()); + return; } Sheet sheet = writeContext.writeSheetHolder().getSheet(); @@ -345,7 +356,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { throw new ExcelGenerateException("The wrong direction."); } - Row row = createRowIfNecessary(sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, isOriginalCell); + Row row = createRowIfNecessary(sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, isOriginalCell, + rowWriteHandlerContext); cellWriteHandlerContext.setRow(row); cellWriteHandlerContext.setRowIndex(lastRowIndex); @@ -375,16 +387,17 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { } private Row createRowIfNecessary(Sheet sheet, Sheet cachedSheet, Integer lastRowIndex, FillConfig fillConfig, - AnalysisCell analysisCell, boolean isOriginalCell) { + AnalysisCell analysisCell, boolean isOriginalCell, RowWriteHandlerContext rowWriteHandlerContext) { + rowWriteHandlerContext.setRowIndex(lastRowIndex); Row row = sheet.getRow(lastRowIndex); if (row != null) { checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); + rowWriteHandlerContext.setRow(row); return row; } row = cachedSheet.getRow(lastRowIndex); if (row == null) { - RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(writeContext, - lastRowIndex, null, Boolean.FALSE); + rowWriteHandlerContext.setRowIndex(lastRowIndex); WriteHandlerUtils.beforeRowCreate(rowWriteHandlerContext); if (fillConfig.getForceNewRow()) { @@ -405,6 +418,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { WriteHandlerUtils.afterRowCreate(rowWriteHandlerContext); } else { checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); + rowWriteHandlerContext.setRow(row); } return row; } diff --git a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java index afef0c97..c3dc708b 100644 --- a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java @@ -58,7 +58,8 @@ public interface RowWriteHandler extends WriteHandler { Integer relativeRowIndex, Boolean isHead) {} /** - * Called after all operations on the row have been completed.This method is not called when fill the data. + * Called after all operations on the row have been completed. + * In the case of the fill , may be called many times. * * @param context */ @@ -68,7 +69,8 @@ public interface RowWriteHandler extends WriteHandler { } /** - * Called after all operations on the row have been completed.This method is not called when fill the data. + * Called after all operations on the row have been completed. + * In the case of the fill , may be called many times. * * @param writeSheetHolder * @param writeTableHolder Nullable.It is null without using table writes. diff --git a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java index 13ebe3cb..961db51f 100644 --- a/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java @@ -1,13 +1,10 @@ package com.alibaba.excel.write.merge; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.util.CellRangeAddress; - import com.alibaba.excel.metadata.property.LoopMergeProperty; -import com.alibaba.excel.write.handler.AbstractRowWriteHandler; import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; -import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import org.apache.poi.ss.util.CellRangeAddress; /** * The regions of the loop merge @@ -55,15 +52,15 @@ public class LoopMergeStrategy implements RowWriteHandler { } @Override - public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Integer relativeRowIndex, Boolean isHead) { - if (isHead) { + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getHead() || context.getRelativeRowIndex() == null) { return; } - if (relativeRowIndex % eachRow == 0) { - CellRangeAddress cellRangeAddress = new CellRangeAddress(row.getRowNum(), row.getRowNum() + eachRow - 1, + if (context.getRelativeRowIndex() % eachRow == 0) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(context.getRowIndex(), + context.getRowIndex() + eachRow - 1, columnIndex, columnIndex + columnExtend - 1); - writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress); + context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress); } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java new file mode 100644 index 00000000..bc4c2483 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java @@ -0,0 +1,36 @@ +package com.alibaba.easyexcel.test.core.fill.annotation; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +public class FillAnnotationData { + @ExcelProperty("日期") + @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") + private Date date; + + @ExcelProperty(value = "数字") + @NumberFormat("#.##%") + private Double number; + + @ContentLoopMerge(columnExtend = 2) + @ExcelProperty("字符串1") + private String string1; + @ExcelProperty("字符串2") + private String string2; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java new file mode 100644 index 00000000..93680d69 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java @@ -0,0 +1,92 @@ +package com.alibaba.easyexcel.test.core.fill.annotation; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class FillAnnotationDataTest { + + private static File file07; + private static File file03; + private static File fileTemplate07; + private static File fileTemplate03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("fillAnnotation07.xlsx"); + file03 = TestFileUtil.createNewFile("fillAnnotation03.xls"); + fileTemplate07 = TestFileUtil.readFile("fill" + File.separator + "annotation.xlsx"); + fileTemplate03 = TestFileUtil.readFile("fill" + File.separator + "annotation.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07, fileTemplate07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03, fileTemplate03); + } + + private void readAndWrite(File file, File fileTemplate) throws Exception { + EasyExcel.write().file(file).head(FillAnnotationData.class).withTemplate(fileTemplate).sheet().doFill(data()); + + Workbook workbook = WorkbookFactory.create(file); + Sheet sheet = workbook.getSheetAt(0); + + Row row1 = sheet.getRow(1); + Assert.assertEquals(2000, row1.getHeight(), 0); + Cell cell10 = row1.getCell(0); + Date date = cell10.getDateCellValue(); + Assert.assertEquals(DateUtils.parseDate("2020-01-01 01:01:01").getTime(), date.getTime()); + String dataFormatString = cell10.getCellStyle().getDataFormatString(); + Assert.assertEquals("yyyy年MM月dd日HH时mm分ss秒", dataFormatString); + Cell cell11 = row1.getCell(1); + Assert.assertEquals(99.99, cell11.getNumericCellValue(), 2); + boolean hasMerge = false; + for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) { + if (mergedRegion.getFirstRow() == 1 && mergedRegion.getLastRow() == 1 + && mergedRegion.getFirstColumn() == 2 && mergedRegion.getLastColumn() == 3) { + hasMerge = true; + } + } + Assert.assertTrue(hasMerge); + } + + private List data() throws Exception { + List list = new ArrayList<>(); + FillAnnotationData data = new FillAnnotationData(); + data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); + data.setNumber(99.99); + data.setString1("string1"); + data.setString2("string2"); + list.add(data); + list.add(data); + list.add(data); + list.add(data); + list.add(data); + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java index 1d97027b..934b62f5 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java @@ -133,7 +133,7 @@ public class TempLargeDataTest { ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, com.alibaba.easyexcel.test.core.large.LargeData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet().build(); - for (int j = 0; j < 2; j++) { + for (int j = 0; j < 5000; j++) { excelWriter.write(data(), writeSheet); } excelWriter.finish(); @@ -141,6 +141,51 @@ public class TempLargeDataTest { }); } + @Test + public void t04WriteExcelNo() throws Exception { + IntStream.rangeClosed(0, 10000).forEach(index -> { + ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, + com.alibaba.easyexcel.test.core.large.LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 50; j++) { + excelWriter.write(data(), writeSheet); + } + excelWriter.finish(); + LOGGER.info("{} 完成", index); + }); + } + + @Test + public void t04WriteExcelPoi() throws Exception { + IntStream.rangeClosed(0, 10000).forEach(index -> { + try (FileOutputStream fileOutputStream = new FileOutputStream(fileWritePoi07)) { + SXSSFWorkbook workbook = new SXSSFWorkbook(500); + //workbook.setCompressTempFiles(true); + SXSSFSheet sheet = workbook.createSheet("sheet1"); + for (int i = 0; i < 100 * 50; i++) { + SXSSFRow row = sheet.createRow(i); + for (int j = 0; j < 25; j++) { + String str = "str-" + j + "-" + i; + //if (i + 10000 == j) { + SXSSFCell cell = row.createCell(j); + cell.setCellValue(str); + //System.out.println(str); + //} + } + if (i % 5000 == 0) { + LOGGER.info("{} write success.", i); + } + } + workbook.write(fileOutputStream); + workbook.dispose(); + workbook.close(); + } catch (Exception e) { + e.printStackTrace(); + } + LOGGER.info("{} 完成", index); + }); + } + private List data() { List list = new ArrayList<>(); diff --git a/src/test/resources/fill/annotation.xls b/src/test/resources/fill/annotation.xls new file mode 100644 index 00000000..8fd19c5a Binary files /dev/null and b/src/test/resources/fill/annotation.xls differ diff --git a/src/test/resources/fill/annotation.xlsx b/src/test/resources/fill/annotation.xlsx new file mode 100644 index 00000000..5395232b Binary files /dev/null and b/src/test/resources/fill/annotation.xlsx differ diff --git a/update.md b/update.md index 7e791d8d..8a695624 100644 --- a/update.md +++ b/update.md @@ -1,5 +1,6 @@ # 3.0.4 * 调整读写默认大小,防止大批量写的时候可能会full gc +* `fill`的情况新增 `afterRowDispose`事件 # 3.0.3 * 修复`HeadStyle`无效的bug