From 6440c302a5ae7c257674f49da9161b8a38455b92 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 10 Aug 2024 23:35:34 +0800 Subject: [PATCH] add ExcelConfig --- .../dromara/hutool/poi/excel/ExcelBase.java | 116 +++++-------- .../dromara/hutool/poi/excel/ExcelConfig.java | 156 ++++++++++++++++++ .../org/dromara/hutool/poi/excel/RowUtil.java | 2 +- .../poi/excel/reader/AbstractSheetReader.java | 127 +++----------- .../poi/excel/reader/BeanSheetReader.java | 32 +--- .../poi/excel/reader/ColumnSheetReader.java | 15 +- .../poi/excel/reader/ConsumerSheetReader.java | 65 ++++++++ .../hutool/poi/excel/reader/ExcelReader.java | 70 +++----- .../poi/excel/reader/ListSheetReader.java | 12 +- .../poi/excel/reader/MapSheetReader.java | 23 ++- .../poi/excel/writer/BigExcelWriter.java | 7 +- .../poi/excel/writer/ExcelWriteConfig.java | 88 ++++++++++ .../hutool/poi/excel/writer/ExcelWriter.java | 116 +++---------- .../poi/excel/reader/CellEditorTest.java | 2 +- .../poi/excel/reader/ExcelReadTest.java | 26 +-- .../poi/excel/writer/BigExcelWriteTest.java | 16 +- .../poi/excel/writer/ExcelWriteTest.java | 67 ++++---- .../poi/excel/writer/Issue2221Test.java | 20 ++- .../poi/excel/writer/IssueI66Z6BTest.java | 5 +- 19 files changed, 539 insertions(+), 426 deletions(-) create mode 100644 hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelConfig.java create mode 100644 hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ConsumerSheetReader.java create mode 100644 hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriteConfig.java diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java index 33761c09f..910051f75 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java @@ -31,18 +31,22 @@ import java.io.Closeable; import java.io.File; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; /** * Excel基础类,用于抽象ExcelWriter和ExcelReader中共用部分的对象和方法 * * @param 子类类型,用于返回this + * @param ExcelConfig类型 * @author looly * @since 4.1.4 */ -public class ExcelBase> implements Closeable { +public class ExcelBase, C extends ExcelConfig> implements Closeable { + + /** + * Excel配置,此项不为空 + */ + protected C config; /** * 是否被关闭 */ @@ -59,22 +63,40 @@ public class ExcelBase> implements Closeable { * Excel中对应的Sheet */ protected Sheet sheet; - /** - * 标题行别名 - */ - protected Map headerAlias; /** * 构造 * + * @param config config * @param sheet Excel中的sheet */ - public ExcelBase(final Sheet sheet) { - Assert.notNull(sheet, "No Sheet provided."); - this.sheet = sheet; + public ExcelBase(final C config, final Sheet sheet) { + this.config = Assert.notNull(config); + this.sheet = Assert.notNull(sheet, "No Sheet provided."); this.workbook = sheet.getWorkbook(); } + /** + * 设置Excel配置 + * + * @param config Excel配置 + * @return this + */ + @SuppressWarnings("unchecked") + public T setConfig(final C config) { + this.config = config; + return (T) this; + } + + /** + * 获取Excel配置 + * + * @return Excel配置 + */ + public C getConfig() { + return this.config; + } + /** * 获取Workbook * @@ -428,24 +450,26 @@ public class ExcelBase> implements Closeable { /** * 创建 {@link Hyperlink},默认内容(标签为链接地址本身) - * @param type 链接类型 + * + * @param type 链接类型 * @param address 链接地址 * @return 链接 * @since 5.7.13 */ - public Hyperlink createHyperlink(final HyperlinkType type, final String address){ + public Hyperlink createHyperlink(final HyperlinkType type, final String address) { return createHyperlink(type, address, address); } /** * 创建 {@link Hyperlink},默认内容 - * @param type 链接类型 + * + * @param type 链接类型 * @param address 链接地址 - * @param label 标签,即单元格中显示的内容 + * @param label 标签,即单元格中显示的内容 * @return 链接 * @since 5.7.13 */ - public Hyperlink createHyperlink(final HyperlinkType type, final String address, final String label){ + public Hyperlink createHyperlink(final HyperlinkType type, final String address, final String label) { final Hyperlink hyperlink = this.workbook.getCreationHelper().createHyperlink(type); hyperlink.setAddress(address); hyperlink.setLabel(label); @@ -572,66 +596,4 @@ public class ExcelBase> implements Closeable { this.workbook = null; this.isClosed = true; } - - /** - * 获得标题行的别名Map - * - * @return 别名Map - */ - public Map getHeaderAlias() { - return headerAlias; - } - - /** - * 设置标题行的别名Map - * - * @param headerAlias 别名Map - * @return this - */ - @SuppressWarnings("unchecked") - public T setHeaderAlias(final Map headerAlias) { - this.headerAlias = headerAlias; - return (T) this; - } - - /** - * 增加标题别名 - * - * @param header 标题 - * @param alias 别名 - * @return this - */ - @SuppressWarnings("unchecked") - public T addHeaderAlias(final String header, final String alias) { - Map headerAlias = this.headerAlias; - if (null == headerAlias) { - headerAlias = new LinkedHashMap<>(); - } - this.headerAlias = headerAlias; - this.headerAlias.put(header, alias); - return (T) this; - } - - /** - * 去除标题别名 - * - * @param header 标题 - * @return this - */ - @SuppressWarnings("unchecked") - public T removeHeaderAlias(final String header) { - this.headerAlias.remove(header); - return (T) this; - } - - /** - * 清空标题别名,key为Map中的key,value为别名 - * - * @return this - */ - @SuppressWarnings("unchecked") - public T clearHeaderAlias() { - this.headerAlias = null; - return (T) this; - } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelConfig.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelConfig.java new file mode 100644 index 000000000..f71ce027a --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelConfig.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.poi.excel; + +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.poi.excel.cell.CellEditor; +import org.dromara.hutool.poi.excel.cell.CellReferenceUtil; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Excel读取和写出通用配置 + * + * @author Looly + * @since 6.0.0 + */ +public class ExcelConfig { + /** + * 标题行别名 + */ + protected Map headerAlias; + /** + * 单元格值处理接口 + */ + protected CellEditor cellEditor; + + /** + * 获得标题行的别名Map + * + * @return 别名Map + */ + public Map getHeaderAlias() { + return headerAlias; + } + + /** + * 设置标题行的别名Map + * + * @param headerAlias 别名Map + * @return this + */ + public ExcelConfig setHeaderAlias(final Map headerAlias) { + this.headerAlias = headerAlias; + return this; + } + + /** + * 增加标题别名 + * + * @param header 标题 + * @param alias 别名 + * @return this + */ + public ExcelConfig addHeaderAlias(final String header, final String alias) { + Map headerAlias = this.headerAlias; + if (null == headerAlias) { + headerAlias = new LinkedHashMap<>(); + this.headerAlias = headerAlias; + } + headerAlias.put(header, alias); + return this; + } + + /** + * 去除标题别名 + * + * @param header 标题 + * @return this + */ + public ExcelConfig removeHeaderAlias(final String header) { + this.headerAlias.remove(header); + return this; + } + + /** + * 清空标题别名,key为Map中的key,value为别名 + * + * @return this + */ + public ExcelConfig clearHeaderAlias() { + return setHeaderAlias(null); + } + + /** + * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header + * + * @param headerList 原标题列表 + * @return 转换别名列表 + */ + public List aliasHeader(final List headerList) { + if (CollUtil.isEmpty(headerList)) { + return new ArrayList<>(0); + } + + final int size = headerList.size(); + final List result = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + result.add(aliasHeader(headerList.get(i), i)); + } + return result; + } + + /** + * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header + * + * @param headerObj 原标题 + * @param index 标题所在列号,当标题为空时,列号对应的字母便是header + * @return 转换别名列表 + */ + public String aliasHeader(final Object headerObj, final int index) { + if (null == headerObj) { + return CellReferenceUtil.indexToColName(index); + } + + final String header = headerObj.toString(); + if (null != this.headerAlias) { + return ObjUtil.defaultIfNull(this.headerAlias.get(header), header); + } + return header; + } + + /** + * 获取单元格值处理器 + * + * @return 单元格值处理器 + */ + public CellEditor getCellEditor() { + return this.cellEditor; + } + + /** + * 设置单元格值处理逻辑
+ * 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 + * + * @param cellEditor 单元格值处理接口 + * @return this + */ + public ExcelConfig setCellEditor(final CellEditor cellEditor) { + this.cellEditor = cellEditor; + return this; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/RowUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/RowUtil.java index 16f4320e6..79c6f24b9 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/RowUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/RowUtil.java @@ -73,7 +73,7 @@ public class RowUtil { */ public static List readRow(final Row row, final int startCellNumInclude, final int endCellNumInclude, final CellEditor cellEditor) { if (null == row) { - return new ArrayList<>(0); + return ListUtil.empty(); } final short rowLength = row.getLastCellNum(); if (rowLength < 0) { diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/AbstractSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/AbstractSheetReader.java index 9379edc93..fe85dcebe 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/AbstractSheetReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/AbstractSheetReader.java @@ -13,16 +13,8 @@ package org.dromara.hutool.poi.excel.reader; import org.apache.poi.ss.usermodel.Sheet; -import org.dromara.hutool.core.collection.CollUtil; -import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.poi.excel.RowUtil; -import org.dromara.hutool.poi.excel.cell.CellEditor; -import org.dromara.hutool.poi.excel.cell.CellReferenceUtil; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import org.apache.poi.ss.util.CellRangeAddress; +import org.dromara.hutool.poi.excel.ExcelConfig; /** * 抽象{@link Sheet}数据读取实现 @@ -33,26 +25,15 @@ import java.util.Map; */ public abstract class AbstractSheetReader implements SheetReader { - /** - * 读取起始行(包含,从0开始计数) - */ - protected final int startRowIndex; - /** - * 读取结束行(包含,从0开始计数) - */ - protected final int endRowIndex; + protected final CellRangeAddress cellRangeAddress; /** * 是否忽略空行 */ protected boolean ignoreEmptyRow = true; /** - * 单元格值处理接口 + * Excel配置 */ - protected CellEditor cellEditor; - /** - * 标题别名 - */ - private Map headerAlias; + protected ExcelConfig config; /** * 构造 @@ -61,18 +42,28 @@ public abstract class AbstractSheetReader implements SheetReader { * @param endRowIndex 结束行(包含,从0开始计数) */ public AbstractSheetReader(final int startRowIndex, final int endRowIndex) { - this.startRowIndex = startRowIndex; - this.endRowIndex = endRowIndex; + this(new CellRangeAddress( + Math.min(startRowIndex, endRowIndex), + Math.max(startRowIndex, endRowIndex), + 0, Integer.MAX_VALUE)); } /** - * 设置单元格值处理逻辑
- * 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 + * 构造 * - * @param cellEditor 单元格值处理接口 + * @param cellRangeAddress 读取范围 */ - public void setCellEditor(final CellEditor cellEditor) { - this.cellEditor = cellEditor; + public AbstractSheetReader(final CellRangeAddress cellRangeAddress) { + this.cellRangeAddress = cellRangeAddress; + } + + /** + * 设置Excel配置 + * + * @param config Excel配置 + */ + public void setExcelConfig(final ExcelConfig config) { + this.config = config; } /** @@ -83,78 +74,4 @@ public abstract class AbstractSheetReader implements SheetReader { public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) { this.ignoreEmptyRow = ignoreEmptyRow; } - - /** - * 设置标题行的别名Map - * - * @param headerAlias 别名Map - */ - public void setHeaderAlias(final Map headerAlias) { - this.headerAlias = headerAlias; - } - - /** - * 增加标题别名 - * - * @param header 标题 - * @param alias 别名 - */ - public void addHeaderAlias(final String header, final String alias) { - Map headerAlias = this.headerAlias; - if (null == headerAlias) { - headerAlias = new LinkedHashMap<>(); - } - this.headerAlias = headerAlias; - this.headerAlias.put(header, alias); - } - - /** - * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header - * - * @param headerList 原标题列表 - * @return 转换别名列表 - */ - protected List aliasHeader(final List headerList) { - if (CollUtil.isEmpty(headerList)) { - return new ArrayList<>(0); - } - - final int size = headerList.size(); - final ArrayList result = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - result.add(aliasHeader(headerList.get(i), i)); - } - return result; - } - - /** - * 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header - * - * @param headerObj 原标题 - * @param index 标题所在列号,当标题为空时,列号对应的字母便是header - * @return 转换别名列表 - * @since 4.3.2 - */ - protected String aliasHeader(final Object headerObj, final int index) { - if (null == headerObj) { - return CellReferenceUtil.indexToColName(index); - } - - final String header = headerObj.toString(); - if(null != this.headerAlias){ - return ObjUtil.defaultIfNull(this.headerAlias.get(header), header); - } - return header; - } - - /** - * 读取某一行数据 - * - * @param sheet {@link Sheet} - * @param rowIndex 行号,从0开始 - * @return 一行数据 - */ - protected List readRow(final Sheet sheet, final int rowIndex) { - return RowUtil.readRow(sheet.getRow(rowIndex), this.cellEditor); - } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/BeanSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/BeanSheetReader.java index 51ab432fe..d01dfd276 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/BeanSheetReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/BeanSheetReader.java @@ -12,10 +12,10 @@ package org.dromara.hutool.poi.excel.reader; +import org.apache.poi.ss.usermodel.Sheet; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.copier.CopyOptions; -import org.dromara.hutool.poi.excel.cell.CellEditor; -import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.poi.excel.ExcelConfig; import java.util.ArrayList; import java.util.List; @@ -63,13 +63,12 @@ public class BeanSheetReader implements SheetReader> { } /** - * 设置单元格值处理逻辑
- * 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 + * 设置Excel配置 * - * @param cellEditor 单元格值处理接口 + * @param config Excel配置 */ - public void setCellEditor(final CellEditor cellEditor) { - this.mapSheetReader.setCellEditor(cellEditor); + public void setExcelConfig(final ExcelConfig config) { + this.mapSheetReader.setExcelConfig(config); } /** @@ -80,23 +79,4 @@ public class BeanSheetReader implements SheetReader> { public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) { this.mapSheetReader.setIgnoreEmptyRow(ignoreEmptyRow); } - - /** - * 设置标题行的别名Map - * - * @param headerAlias 别名Map - */ - public void setHeaderAlias(final Map headerAlias) { - this.mapSheetReader.setHeaderAlias(headerAlias); - } - - /** - * 增加标题别名 - * - * @param header 标题 - * @param alias 别名 - */ - public void addHeaderAlias(final String header, final String alias) { - this.mapSheetReader.addHeaderAlias(header, alias); - } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ColumnSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ColumnSheetReader.java index f69592ac7..a8ab4872f 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ColumnSheetReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ColumnSheetReader.java @@ -12,8 +12,10 @@ package org.dromara.hutool.poi.excel.reader; -import org.dromara.hutool.poi.excel.cell.CellUtil; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.dromara.hutool.poi.excel.cell.CellEditor; +import org.dromara.hutool.poi.excel.cell.CellUtil; import java.util.ArrayList; import java.util.List; @@ -26,8 +28,6 @@ import java.util.List; */ public class ColumnSheetReader extends AbstractSheetReader> { - private final int columnIndex; - /** * 构造 * @@ -36,17 +36,18 @@ public class ColumnSheetReader extends AbstractSheetReader> { * @param endRowIndex 结束行(包含,从0开始计数) */ public ColumnSheetReader(final int columnIndex, final int startRowIndex, final int endRowIndex) { - super(startRowIndex, endRowIndex); - this.columnIndex = columnIndex; + super(new CellRangeAddress(startRowIndex, endRowIndex, columnIndex, columnIndex)); } @Override public List read(final Sheet sheet) { final List resultList = new ArrayList<>(); - final int startRowIndex = Math.max(this.startRowIndex, sheet.getFirstRowNum());// 读取起始行(包含) - final int endRowIndex = Math.min(this.endRowIndex, sheet.getLastRowNum());// 读取结束行(包含) + final int startRowIndex = Math.max(this.cellRangeAddress.getFirstRow(), sheet.getFirstRowNum());// 读取起始行(包含) + final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), sheet.getLastRowNum());// 读取结束行(包含) + final int columnIndex = this.cellRangeAddress.getFirstColumn(); + final CellEditor cellEditor = this.config.getCellEditor(); Object value; for (int i = startRowIndex; i <= endRowIndex; i++) { value = CellUtil.getCellValue(CellUtil.getCell(sheet.getRow(i), columnIndex), cellEditor); diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ConsumerSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ConsumerSheetReader.java new file mode 100644 index 000000000..1b191e850 --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ConsumerSheetReader.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.poi.excel.reader; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.core.func.SerBiConsumer; +import org.dromara.hutool.poi.excel.cell.CellUtil; + +/** + * 读取Excel的Sheet,使用Consumer方式处理单元格 + * + * @author Looly + * @since 6.0.0 + */ +public class ConsumerSheetReader extends AbstractSheetReader { + + + private final SerBiConsumer cellHandler; + + /** + * 构造 + * + * @param startRowIndex 起始行(包含,从0开始计数) + * @param endRowIndex 结束行(包含,从0开始计数) + * @param cellHandler 单元格处理器,用于处理读到的单元格及其数据 + */ + public ConsumerSheetReader(final int startRowIndex, final int endRowIndex, final SerBiConsumer cellHandler) { + super(startRowIndex, endRowIndex); + this.cellHandler = cellHandler; + } + + @Override + public Void read(final Sheet sheet) { + final int startRowIndex = Math.max(this.cellRangeAddress.getFirstRow(), sheet.getFirstRowNum());// 读取起始行(包含) + final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), sheet.getLastRowNum());// 读取结束行(包含) + + Row row; + for (int y = startRowIndex; y <= endRowIndex; y++) { + row = sheet.getRow(y); + if (null != row) { + final short startColumnIndex = (short) Math.max(this.cellRangeAddress.getFirstColumn(), row.getFirstCellNum()); + final short endColumnIndex = (short) Math.min(this.cellRangeAddress.getLastColumn(), row.getLastCellNum()); + Cell cell; + for (short x = startColumnIndex; x < endColumnIndex; x++) { + cell = row.getCell(x); + cellHandler.accept(cell, CellUtil.getCellValue(cell)); + } + } + } + + return null; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java index 4181b978d..cb73f4b8d 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java @@ -12,18 +12,17 @@ package org.dromara.hutool.poi.excel.reader; -import org.dromara.hutool.core.io.IoUtil; -import org.dromara.hutool.core.io.file.FileUtil; -import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.func.SerBiConsumer; -import org.dromara.hutool.poi.excel.*; -import org.dromara.hutool.poi.excel.cell.CellEditor; -import org.dromara.hutool.poi.excel.cell.CellUtil; import org.apache.poi.ss.extractor.ExcelExtractor; 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.dromara.hutool.core.func.SerBiConsumer; +import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.poi.excel.*; +import org.dromara.hutool.poi.excel.cell.CellUtil; import org.dromara.hutool.poi.excel.writer.ExcelWriter; import java.io.File; @@ -38,16 +37,12 @@ import java.util.Map; * @author Looly * @since 3.1.0 */ -public class ExcelReader extends ExcelBase { +public class ExcelReader extends ExcelBase { /** * 是否忽略空行 */ private boolean ignoreEmptyRow = true; - /** - * 单元格值处理接口 - */ - private CellEditor cellEditor; // ------------------------------------------------------------------------------------------------------- Constructor start /** @@ -139,7 +134,7 @@ public class ExcelReader extends ExcelBase { * @param sheet Excel中的sheet */ public ExcelReader(final Sheet sheet) { - super(sheet); + super(new ExcelConfig(), sheet); } // ------------------------------------------------------------------------------------------------------- Constructor end @@ -165,17 +160,6 @@ public class ExcelReader extends ExcelBase { return this; } - /** - * 设置单元格值处理逻辑
- * 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 - * - * @param cellEditor 单元格值处理接口 - * @return this - */ - public ExcelReader setCellEditor(final CellEditor cellEditor) { - this.cellEditor = cellEditor; - return this; - } // ------------------------------------------------------------------------------------------------------- Getters and Setters end /** @@ -220,9 +204,8 @@ public class ExcelReader extends ExcelBase { */ public List> read(final int startRowIndex, final int endRowIndex, final boolean aliasFirstLine) { final ListSheetReader reader = new ListSheetReader(startRowIndex, endRowIndex, aliasFirstLine); - reader.setCellEditor(this.cellEditor); + reader.setExcelConfig(this.config); reader.setIgnoreEmptyRow(this.ignoreEmptyRow); - reader.setHeaderAlias(headerAlias); return read(reader); } @@ -249,9 +232,8 @@ public class ExcelReader extends ExcelBase { */ public List readColumn(final int columnIndex, final int startRowIndex, final int endRowIndex) { final ColumnSheetReader reader = new ColumnSheetReader(columnIndex, startRowIndex, endRowIndex); - reader.setCellEditor(this.cellEditor); + reader.setExcelConfig(this.config); reader.setIgnoreEmptyRow(this.ignoreEmptyRow); - reader.setHeaderAlias(headerAlias); return read(reader); } @@ -275,25 +257,13 @@ public class ExcelReader extends ExcelBase { * @param cellHandler 单元格处理器,用于处理读到的单元格及其数据 * @since 5.3.8 */ - public void read(int startRowIndex, int endRowIndex, final SerBiConsumer cellHandler) { + public void read(final int startRowIndex, final int endRowIndex, final SerBiConsumer cellHandler) { checkNotClosed(); - startRowIndex = Math.max(startRowIndex, this.sheet.getFirstRowNum());// 读取起始行(包含) - endRowIndex = Math.min(endRowIndex, this.sheet.getLastRowNum());// 读取结束行(包含) - - Row row; - short columnSize; - for (int y = startRowIndex; y <= endRowIndex; y++) { - row = this.sheet.getRow(y); - if (null != row) { - columnSize = row.getLastCellNum(); - Cell cell; - for (short x = 0; x < columnSize; x++) { - cell = row.getCell(x); - cellHandler.accept(cell, CellUtil.getCellValue(cell)); - } - } - } + final ConsumerSheetReader reader = new ConsumerSheetReader(startRowIndex, endRowIndex, cellHandler); + reader.setExcelConfig(this.config); + reader.setIgnoreEmptyRow(this.ignoreEmptyRow); + reader.read(sheet); } /** @@ -317,9 +287,8 @@ public class ExcelReader extends ExcelBase { */ public List> read(final int headerRowIndex, final int startRowIndex, final int endRowIndex) { final MapSheetReader reader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex); - reader.setCellEditor(this.cellEditor); + reader.setExcelConfig(this.config); reader.setIgnoreEmptyRow(this.ignoreEmptyRow); - reader.setHeaderAlias(headerAlias); return read(reader); } @@ -360,9 +329,8 @@ public class ExcelReader extends ExcelBase { */ public List read(final int headerRowIndex, final int startRowIndex, final int endRowIndex, final Class beanType) { final BeanSheetReader reader = new BeanSheetReader<>(headerRowIndex, startRowIndex, endRowIndex, beanType); - reader.setCellEditor(this.cellEditor); + reader.setExcelConfig(this.config); reader.setIgnoreEmptyRow(this.ignoreEmptyRow); - reader.setHeaderAlias(headerAlias); return read(reader); } @@ -421,7 +389,7 @@ public class ExcelReader extends ExcelBase { * @since 4.0.3 */ public Object readCellValue(final int x, final int y) { - return CellUtil.getCellValue(getCell(x, y), this.cellEditor); + return CellUtil.getCellValue(getCell(x, y), this.config.getCellEditor()); } /** @@ -452,7 +420,7 @@ public class ExcelReader extends ExcelBase { * @return 单元格值列表 */ private List readRow(final Row row) { - return RowUtil.readRow(row, this.cellEditor); + return RowUtil.readRow(row, this.config.getCellEditor()); } /** diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ListSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ListSheetReader.java index f6926cba4..7ae66e9ba 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ListSheetReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ListSheetReader.java @@ -15,6 +15,8 @@ package org.dromara.hutool.poi.excel.reader; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.convert.Convert; import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.poi.excel.RowUtil; +import org.dromara.hutool.poi.excel.cell.CellEditor; import java.util.ArrayList; import java.util.List; @@ -46,15 +48,17 @@ public class ListSheetReader extends AbstractSheetReader>> { public List> read(final Sheet sheet) { final List> resultList = new ArrayList<>(); - final int startRowIndex = Math.max(this.startRowIndex, sheet.getFirstRowNum());// 读取起始行(包含) - final int endRowIndex = Math.min(this.endRowIndex, sheet.getLastRowNum());// 读取结束行(包含) + final int startRowIndex = Math.max(this.cellRangeAddress.getFirstRow(), sheet.getFirstRowNum());// 读取起始行(包含) + final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), sheet.getLastRowNum());// 读取结束行(包含) + List rowList; + final CellEditor cellEditor = this.config.getCellEditor(); for (int i = startRowIndex; i <= endRowIndex; i++) { - rowList = readRow(sheet, i); + rowList = RowUtil.readRow(sheet.getRow(i), cellEditor); if (CollUtil.isNotEmpty(rowList) || !ignoreEmptyRow) { if (aliasFirstLine && i == startRowIndex) { // 第一行作为标题行,替换别名 - rowList = Convert.toList(Object.class, aliasHeader(rowList)); + rowList = Convert.toList(Object.class, this.config.aliasHeader(rowList)); } resultList.add(rowList); } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/MapSheetReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/MapSheetReader.java index 84adbf7ff..c30a57e81 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/MapSheetReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/MapSheetReader.java @@ -17,6 +17,7 @@ import org.dromara.hutool.core.collection.iter.IterUtil; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.text.StrUtil; import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.poi.excel.RowUtil; import java.util.ArrayList; import java.util.List; @@ -57,15 +58,18 @@ public class MapSheetReader extends AbstractSheetReader throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is lower than first row index {}.", headerRowIndex, firstRowNum)); } else if (headerRowIndex > lastRowNum) { throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is greater than last row index {}.", headerRowIndex, lastRowNum)); - } else if (startRowIndex > lastRowNum) { + } + + int startRowIndex = this.cellRangeAddress.getFirstRow(); + if (startRowIndex > lastRowNum) { // issue#I5U1JA 只有标题行的Excel,起始行是1,标题行(最后的行号是0) return ListUtil.empty(); } - final int startRowIndex = Math.max(this.startRowIndex, firstRowNum);// 读取起始行(包含) - final int endRowIndex = Math.min(this.endRowIndex, lastRowNum);// 读取结束行(包含) + startRowIndex = Math.max(startRowIndex, firstRowNum);// 读取起始行(包含) + final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), lastRowNum);// 读取结束行(包含) // 读取header - final List headerList = aliasHeader(readRow(sheet, headerRowIndex)); + final List headerList = this.config.aliasHeader(readRow(sheet, headerRowIndex)); final List> result = new ArrayList<>(endRowIndex - startRowIndex + 1); List rowList; @@ -80,4 +84,15 @@ public class MapSheetReader extends AbstractSheetReader } return result; } + + /** + * 读取某一行数据 + * + * @param sheet {@link Sheet} + * @param rowIndex 行号,从0开始 + * @return 一行数据 + */ + private List readRow(final Sheet sheet, final int rowIndex) { + return RowUtil.readRow(sheet.getRow(rowIndex), this.config.getCellEditor()); + } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/BigExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/BigExcelWriter.java index 69ab51cfc..22ee9ef64 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/BigExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/BigExcelWriter.java @@ -12,6 +12,7 @@ package org.dromara.hutool.poi.excel.writer; +import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.apache.poi.ss.usermodel.Sheet; @@ -32,6 +33,9 @@ import java.io.OutputStream; */ public class BigExcelWriter extends ExcelWriter { + /** + * 默认内存中保存的行数,默认100 + */ public static final int DEFAULT_WINDOW_SIZE = SXSSFWorkbook.DEFAULT_WINDOW_SIZE; /** @@ -181,6 +185,7 @@ public class BigExcelWriter extends ExcelWriter { return this; } + @SuppressWarnings("resource") @Override public void close() { if (null != this.destFile && !isFlushed) { @@ -188,7 +193,7 @@ public class BigExcelWriter extends ExcelWriter { } // 清理临时文件 - ((SXSSFWorkbook) this.workbook).dispose(); + IoUtil.closeIfPossible(this.workbook); super.closeWithoutFlush(); } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriteConfig.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriteConfig.java new file mode 100644 index 000000000..a8582e3b4 --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriteConfig.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.poi.excel.writer; + +import org.dromara.hutool.core.comparator.IndexedComparator; +import org.dromara.hutool.core.map.MapUtil; +import org.dromara.hutool.poi.excel.ExcelConfig; + +import java.util.Comparator; +import java.util.Map; +import java.util.Set; + +/** + * Excel写出配置 + * + * @author Looly + * @since 6.0.0 + */ +public class ExcelWriteConfig extends ExcelConfig { + + /** + * 是否只保留别名对应的字段 + */ + protected boolean onlyAlias; + /** + * 标题顺序比较器 + */ + protected Comparator aliasComparator; + + @Override + public ExcelWriteConfig setHeaderAlias(final Map headerAlias) { + this.aliasComparator = null; + return (ExcelWriteConfig) super.setHeaderAlias(headerAlias); + } + + @Override + public ExcelWriteConfig addHeaderAlias(final String header, final String alias) { + this.aliasComparator = null; + return (ExcelWriteConfig) super.addHeaderAlias(header, alias); + } + + @Override + public ExcelWriteConfig removeHeaderAlias(final String header) { + this.aliasComparator = null; + return (ExcelWriteConfig) super.removeHeaderAlias(header); + } + + /** + * 设置是否只保留别名中的字段值,如果为true,则不设置alias的字段将不被输出,false表示原样输出 + * Bean中设置@Alias时,setOnlyAlias是无效的,这个参数只和addHeaderAlias配合使用,原因是注解是Bean内部的操作,而addHeaderAlias是Writer的操作,不互通。 + * + * @param isOnlyAlias 是否只保留别名中的字段值 + * @return this + */ + public ExcelWriteConfig setOnlyAlias(final boolean isOnlyAlias) { + this.onlyAlias = isOnlyAlias; + return this; + } + + /** + * 获取单例的别名比较器,比较器的顺序为别名加入的顺序 + * + * @return Comparator + */ + public Comparator getCachedAliasComparator() { + final Map headerAlias = this.headerAlias; + if (MapUtil.isEmpty(headerAlias)) { + return null; + } + Comparator aliasComparator = this.aliasComparator; + if (null == aliasComparator) { + final Set keySet = headerAlias.keySet(); + aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0])); + this.aliasComparator = aliasComparator; + } + return aliasComparator; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java index 1d63b697b..9810bde6d 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java @@ -22,7 +22,6 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.collection.ListUtil; -import org.dromara.hutool.core.comparator.IndexedComparator; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; @@ -63,16 +62,8 @@ import java.util.concurrent.atomic.AtomicInteger; * @since 3.2.0 */ @SuppressWarnings("resource") -public class ExcelWriter extends ExcelBase { +public class ExcelWriter extends ExcelBase { - /** - * 是否只保留别名对应的字段 - */ - private boolean onlyAlias; - /** - * 标题顺序比较器 - */ - private Comparator aliasComparator; /** * 样式集,定义不同类型数据样式 */ @@ -81,10 +72,6 @@ public class ExcelWriter extends ExcelBase { * 标题项对应列号缓存,每次写标题更新此缓存 */ private Map headLocationCache; - /** - * 单元格值处理接口 - */ - private CellEditor cellEditor; /** * 当前行 */ @@ -188,22 +175,16 @@ public class ExcelWriter extends ExcelBase { * @since 4.0.6 */ public ExcelWriter(final Sheet sheet) { - super(sheet); + super(new ExcelWriteConfig(), sheet); this.styleSet = new DefaultStyleSet(workbook); this.currentRow = new AtomicInteger(0); } // endregion - /** - * 设置单元格值处理逻辑
- * 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等 - * - * @param cellEditor 单元格值处理接口 - * @return this - */ - public ExcelWriter setCellEditor(final CellEditor cellEditor) { - this.cellEditor = cellEditor; - return this; + + @Override + public ExcelWriter setConfig(final ExcelWriteConfig config) { + return super.setConfig(config); } @Override @@ -410,39 +391,6 @@ public class ExcelWriter extends ExcelBase { } //region header alias - @Override - public ExcelWriter setHeaderAlias(final Map headerAlias) { - // 新增别名时清除比较器缓存 - this.aliasComparator = null; - return super.setHeaderAlias(headerAlias); - } - - @Override - public ExcelWriter clearHeaderAlias() { - // 清空别名时清除比较器缓存 - this.aliasComparator = null; - return super.clearHeaderAlias(); - } - - @Override - public ExcelWriter addHeaderAlias(final String name, final String alias) { - // 新增别名时清除比较器缓存 - this.aliasComparator = null; - return super.addHeaderAlias(name, alias); - } - - /** - * 设置是否只保留别名中的字段值,如果为true,则不设置alias的字段将不被输出,false表示原样输出 - * Bean中设置@Alias时,setOnlyAlias是无效的,这个参数只和addHeaderAlias配合使用,原因是注解是Bean内部的操作,而addHeaderAlias是Writer的操作,不互通。 - * - * @param isOnlyAlias 是否只保留别名中的字段值 - * @return this - * @since 4.1.22 - */ - public ExcelWriter setOnlyAlias(final boolean isOnlyAlias) { - this.onlyAlias = isOnlyAlias; - return this; - } //endregion /** @@ -704,7 +652,7 @@ public class ExcelWriter extends ExcelBase { // 设置内容 if (null != content) { final Cell cell = getOrCreateCell(cellRangeAddress.getFirstColumn(), cellRangeAddress.getFirstRow()); - CellUtil.setCellValue(cell, content, cellStyle, this.cellEditor); + CellUtil.setCellValue(cell, content, cellStyle, this.config.getCellEditor()); } return this; } @@ -911,11 +859,12 @@ public class ExcelWriter extends ExcelBase { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); this.headLocationCache = new SafeConcurrentHashMap<>(); final Row row = this.sheet.createRow(this.currentRow.getAndIncrement()); + final CellEditor cellEditor = this.config.getCellEditor(); int i = 0; Cell cell; for (final Object value : rowData) { cell = row.createCell(i); - CellUtil.setCellValue(cell, value, this.styleSet, true, this.cellEditor); + CellUtil.setCellValue(cell, value, this.styleSet, true, cellEditor); this.headLocationCache.put(StrUtil.toString(value), i); i++; } @@ -939,6 +888,7 @@ public class ExcelWriter extends ExcelBase { final Iterator iterator = rowData.iterator(); //如果获取的row存在单元格,则执行复杂表头逻辑,否则直接调用writeHeadRow(Iterable rowData) if (row.getLastCellNum() != 0) { + final CellEditor cellEditor = this.config.getCellEditor(); for (int i = 0; i < this.workbook.getSpreadsheetVersion().getMaxColumns(); i++) { Cell cell = row.getCell(i); if (cell != null) { @@ -946,7 +896,7 @@ public class ExcelWriter extends ExcelBase { } if (iterator.hasNext()) { cell = row.createCell(i); - CellUtil.setCellValue(cell, iterator.next(), this.styleSet, true, this.cellEditor); + CellUtil.setCellValue(cell, iterator.next(), this.styleSet, true, cellEditor); } else { break; } @@ -975,10 +925,12 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings({"rawtypes", "unchecked"}) public ExcelWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) { + final ExcelWriteConfig config = this.config; + final Map rowMap; if (rowBean instanceof Map) { - if (MapUtil.isNotEmpty(this.headerAlias)) { - rowMap = MapUtil.newTreeMap((Map) rowBean, getCachedAliasComparator()); + if (MapUtil.isNotEmpty(config.getHeaderAlias())) { + rowMap = MapUtil.newTreeMap((Map) rowBean, config.getCachedAliasComparator()); } else { rowMap = (Map) rowBean; } @@ -990,11 +942,11 @@ public class ExcelWriter extends ExcelBase { // Hyperlink当成一个值 return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); } else if (BeanUtil.isReadableBean(rowBean.getClass())) { - if (MapUtil.isEmpty(this.headerAlias)) { + if (MapUtil.isEmpty(config.getHeaderAlias())) { rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); } else { // 别名存在情况下按照别名的添加顺序排序Bean数据 - rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(getCachedAliasComparator()), false, false); + rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(config.getCachedAliasComparator()), false, false); } } else { // 其它转为字符串默认输出 @@ -1033,6 +985,7 @@ public class ExcelWriter extends ExcelBase { // 如果已经写出标题行,根据标题行找对应的值写入 if (MapUtil.isNotEmpty(this.headLocationCache)) { final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); + final CellEditor cellEditor = this.config.getCellEditor(); Integer location; for (final Table.Cell cell : aliasTable) { // 首先查找原名对应的列号 @@ -1042,7 +995,7 @@ public class ExcelWriter extends ExcelBase { location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey())); } if (null != location) { - CellUtil.setCellValue(CellUtil.getOrCreateCell(row, location), cell.getValue(), this.styleSet, false, this.cellEditor); + CellUtil.setCellValue(CellUtil.getOrCreateCell(row, location), cell.getValue(), this.styleSet, false, cellEditor); } } } else { @@ -1061,7 +1014,7 @@ public class ExcelWriter extends ExcelBase { */ public ExcelWriter writeRow(final Iterable rowData) { Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - RowUtil.writeRow(this.sheet.createRow(this.currentRow.getAndIncrement()), rowData, this.styleSet, false, this.cellEditor); + RowUtil.writeRow(this.sheet.createRow(this.currentRow.getAndIncrement()), rowData, this.styleSet, false, this.config.getCellEditor()); return this; } // endregion @@ -1185,7 +1138,7 @@ public class ExcelWriter extends ExcelBase { */ public ExcelWriter writeCellValue(final int x, final int y, final Object value, final boolean isHeader) { final Cell cell = getOrCreateCell(x, y); - CellUtil.setCellValue(cell, value, this.styleSet, isHeader, this.cellEditor); + CellUtil.setCellValue(cell, value, this.styleSet, isHeader, this.config.getCellEditor()); return this; } // endregion @@ -1406,15 +1359,17 @@ public class ExcelWriter extends ExcelBase { */ private Table aliasTable(final Map rowMap) { final Table filteredTable = new RowKeyTable<>(new LinkedHashMap<>(), TableMap::new); - if (MapUtil.isEmpty(this.headerAlias)) { + final Map headerAlias = this.config.getHeaderAlias(); + final boolean onlyAlias = this.config.onlyAlias; + if (MapUtil.isEmpty(headerAlias)) { rowMap.forEach((key, value) -> filteredTable.put(key, key, value)); } else { rowMap.forEach((key, value) -> { - final String aliasName = this.headerAlias.get(StrUtil.toString(key)); + final String aliasName = headerAlias.get(StrUtil.toString(key)); if (null != aliasName) { // 别名键值对加入 filteredTable.put(key, aliasName, value); - } else if (!this.onlyAlias) { + } else if (!onlyAlias) { // 保留无别名设置的键值对 filteredTable.put(key, key, value); } @@ -1423,24 +1378,5 @@ public class ExcelWriter extends ExcelBase { return filteredTable; } - - /** - * 获取单例的别名比较器,比较器的顺序为别名加入的顺序 - * - * @return Comparator - * @since 4.1.5 - */ - private Comparator getCachedAliasComparator() { - if (MapUtil.isEmpty(this.headerAlias)) { - return null; - } - Comparator aliasComparator = this.aliasComparator; - if (null == aliasComparator) { - final Set keySet = this.headerAlias.keySet(); - aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0])); - this.aliasComparator = aliasComparator; - } - return aliasComparator; - } // endregion } diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/CellEditorTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/CellEditorTest.java index bcacecd55..638058c2a 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/CellEditorTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/CellEditorTest.java @@ -27,7 +27,7 @@ public class CellEditorTest { @org.junit.jupiter.api.Test public void readTest(){ final ExcelReader excelReader= ExcelUtil.getReader("cell_editor_test.xlsx"); - excelReader.setCellEditor(new ExcelHandler()); + excelReader.getConfig().setCellEditor(new ExcelHandler()); final List excelReaderObjects=excelReader.readAll(Test.class); Assertions.assertEquals("0", excelReaderObjects.get(0).getTest1()); diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/ExcelReadTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/ExcelReadTest.java index 6b5c2394e..627965f6c 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/ExcelReadTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/reader/ExcelReadTest.java @@ -19,6 +19,7 @@ import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.util.ObjUtil; import lombok.Data; import org.apache.poi.ss.usermodel.Cell; +import org.dromara.hutool.poi.excel.ExcelConfig; import org.dromara.hutool.poi.excel.ExcelUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; @@ -48,7 +49,7 @@ public class ExcelReadTest { headerAlias.put("库房", "storageName"); headerAlias.put("盘点权限", "checkPerm"); headerAlias.put("领料审批权限", "allotAuditPerm"); - reader.setHeaderAlias(headerAlias); + reader.getConfig().setHeaderAlias(headerAlias); // 读取list时默认首个非空行为标题 final List> read = reader.read(0, Integer.MAX_VALUE, true); @@ -146,10 +147,11 @@ public class ExcelReadTest { @Test public void excelReadToBeanListTest() { final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("aaa.xlsx")); - reader.addHeaderAlias("姓名", "name"); - reader.addHeaderAlias("年龄", "age"); - reader.addHeaderAlias("性别", "gender"); - reader.addHeaderAlias("鞋码", "shoeSize"); + final ExcelConfig config = reader.getConfig(); + config.addHeaderAlias("姓名", "name"); + config.addHeaderAlias("年龄", "age"); + config.addHeaderAlias("性别", "gender"); + config.addHeaderAlias("鞋码", "shoeSize"); final List all = reader.readAll(Person.class); Assertions.assertEquals("张三", all.get(0).getName()); @@ -162,9 +164,10 @@ public class ExcelReadTest { @Disabled public void excelReadToBeanListTest2() { final ExcelReader reader = ExcelUtil.getReader("f:/test/toBean.xlsx"); - reader.addHeaderAlias("姓名", "name"); - reader.addHeaderAlias("年龄", "age"); - reader.addHeaderAlias("性别", "gender"); + final ExcelConfig config = reader.getConfig(); + config.addHeaderAlias("姓名", "name"); + config.addHeaderAlias("年龄", "age"); + config.addHeaderAlias("性别", "gender"); final List all = reader.read(0, 2, Person.class); for (final Person person : all) { @@ -220,7 +223,8 @@ public class ExcelReadTest { @Test public void nullValueEditTest(){ final ExcelReader reader = ExcelUtil.getReader("null_cell_test.xlsx"); - reader.setCellEditor((cell, value)-> ObjUtil.defaultIfNull(value, "#")); + final ExcelConfig config = reader.getConfig(); + config.setCellEditor((cell, value)-> ObjUtil.defaultIfNull(value, "#")); final List> read = reader.read(); // 对于任意一个单元格有值的情况下,之前的单元格值按照null处理 @@ -271,14 +275,14 @@ public class ExcelReadTest { //https://gitee.com/dromara/hutool/issues/I5OSFC final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("read.xlsx")); final List> read = reader.read(1,2,2); - for (Map map : read) { + for (final Map map : read) { Console.log(map); } //超出lastIndex 抛出相应提示:startRowIndex row index 4 is greater than last row index 2. //而非:Illegal Capacity: -1 try { final List> readGreaterIndex = reader.read(1,4,4); - } catch (Exception e) { + } catch (final Exception e) { Console.log(e.toString()); } } diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/BigExcelWriteTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/BigExcelWriteTest.java index db400fc72..223d33993 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/BigExcelWriteTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/BigExcelWriteTest.java @@ -191,11 +191,12 @@ public class BigExcelWriteTest { FileUtil.del(FileUtil.file(file)); final BigExcelWriter writer = ExcelUtil.getBigWriter(file); //自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); - writer.addHeaderAlias("score", "分数"); - writer.addHeaderAlias("isPass", "是否通过"); - writer.addHeaderAlias("examDate", "考试时间"); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); + config.addHeaderAlias("score", "分数"); + config.addHeaderAlias("isPass", "是否通过"); + config.addHeaderAlias("examDate", "考试时间"); // 合并单元格后的标题行,使用默认标题样式 writer.merge(4, "一班成绩单"); // 一次性写出内容,使用默认样式 @@ -234,8 +235,9 @@ public class BigExcelWriteTest { final String path = "d:/test/issue1210.xlsx"; FileUtil.del(FileUtil.file(path)); final BigExcelWriter writer = ExcelUtil.getBigWriter(path); - writer.addHeaderAlias("id", "SN"); - writer.addHeaderAlias("userName", "User Name"); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("id", "SN"); + config.addHeaderAlias("userName", "User Name"); final List> list = new ArrayList<>(); list.add(new HashMap() { diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java index d5dd5c500..61d0e9f7f 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java @@ -306,11 +306,12 @@ public class ExcelWriteTest { FileUtil.del(FileUtil.file(file)); final ExcelWriter writer = ExcelUtil.getWriter(file); // 自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); - writer.addHeaderAlias("score", "分数"); - writer.addHeaderAlias("isPass", "是否通过"); - writer.addHeaderAlias("examDate", "考试时间"); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); + config.addHeaderAlias("score", "分数"); + config.addHeaderAlias("isPass", "是否通过"); + config.addHeaderAlias("examDate", "考试时间"); // 合并单元格后的标题行,使用默认标题样式 writer.merge(4, "一班成绩单"); // 一次性写出内容,使用默认样式 @@ -340,10 +341,11 @@ public class ExcelWriteTest { final String file = "f:/test/test_alias.xlsx"; FileUtil.del(FileUtil.file(file)); final ExcelWriter writer = ExcelUtil.getWriter(file); - writer.setOnlyAlias(true); + final ExcelWriteConfig config = writer.getConfig(); + config.setOnlyAlias(true); // 自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); // 合并单元格后的标题行,使用默认标题样式 writer.merge(4, "一班成绩单"); // 一次性写出内容,使用默认样式 @@ -372,10 +374,11 @@ public class ExcelWriteTest { // 通过工具类创建writer final String file = "d:/test/test_alias.xls"; final ExcelWriter writer = ExcelUtil.getWriter(file, "test1"); -// writer.setOnlyAlias(true); // 自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); + final ExcelWriteConfig config = writer.getConfig(); +// writer.setOnlyAlias(true); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); // 一次性写出内容,使用默认样式 writer.write(rows, true); // 关闭writer,释放内存 @@ -403,12 +406,13 @@ public class ExcelWriteTest { // 通过工具类创建writer final String file = "d:/test/test_alias.xls"; final ExcelWriter writer = ExcelUtil.getWriter(file, "test1"); - writer.setOnlyAlias(true); + final ExcelWriteConfig config = writer.getConfig(); + config.setOnlyAlias(true); // 自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); - writer.addHeaderAlias("examDate", "考试时间"); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); + config.addHeaderAlias("examDate", "考试时间"); // 一次性写出内容,使用默认样式 writer.write(rows, true); @@ -438,12 +442,13 @@ public class ExcelWriteTest { final String file = "e:/writeBeanTest.xlsx"; FileUtil.del(FileUtil.file(file)); final ExcelWriter writer = ExcelUtil.getWriter(file); + final ExcelWriteConfig config = writer.getConfig(); // 自定义标题 - writer.addHeaderAlias("name", "姓名"); - writer.addHeaderAlias("age", "年龄"); - writer.addHeaderAlias("score", "分数"); - writer.addHeaderAlias("isPass", "是否通过"); - writer.addHeaderAlias("examDate", "考试时间"); + config.addHeaderAlias("name", "姓名"); + config.addHeaderAlias("age", "年龄"); + config.addHeaderAlias("score", "分数"); + config.addHeaderAlias("isPass", "是否通过"); + config.addHeaderAlias("examDate", "考试时间"); // 合并单元格后的标题行,使用默认标题样式 writer.merge(4, "一班成绩单"); // 一次性写出内容,使用默认样式 @@ -471,9 +476,10 @@ public class ExcelWriteTest { FileUtil.del(FileUtil.file(file)); final ExcelWriter writer = ExcelUtil.getWriter(file); // 自定义标题 - writer.addHeaderAlias("id", "编号"); - writer.addHeaderAlias("num", "序号"); - writer.addHeaderAlias("body", "内容"); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("id", "编号"); + config.addHeaderAlias("num", "序号"); + config.addHeaderAlias("body", "内容"); // 一次性写出内容,使用默认样式 writer.write(rows, true); // 关闭writer,释放内存 @@ -529,18 +535,19 @@ public class ExcelWriteTest { rows.add(tempList); } final ExcelWriter writer = ExcelUtil.getWriter("D:\\test\\multiSheet.xlsx", "正常数据"); - writer.addHeaderAlias("1", "row1"); - writer.addHeaderAlias("3", "row2"); - writer.setOnlyAlias(true); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("1", "row1"); + config.addHeaderAlias("3", "row2"); + config.setOnlyAlias(true); writer.write(rows, true); writer.autoSizeColumnAll(false, 0); //表2 writer.setSheet("当前重复数据"); - writer.clearHeaderAlias(); - writer.addHeaderAlias("3", "行3"); - writer.addHeaderAlias("1", "行1"); + config.clearHeaderAlias(); + config.addHeaderAlias("3", "行3"); + config.addHeaderAlias("1", "行1"); writer.write(rows, true); writer.autoSizeColumnAll(false, 0); @@ -749,7 +756,7 @@ public class ExcelWriteTest { //通过工具类创建writer FileUtil.del(FileUtil.file("d:/test/writeTest2123.xlsx")); final ExcelWriter writer = ExcelUtil.getWriter("d:/test/writeTest2123.xlsx"); - writer.addHeaderAlias("xmnf", "项目年份");//1 + writer.getConfig().addHeaderAlias("xmnf", "项目年份");//1 //合并单元格后的标题行,使用默认标题样式 writer.merge(7, "测试标题"); diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/Issue2221Test.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/Issue2221Test.java index 3602d5f81..f3a3db124 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/Issue2221Test.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/Issue2221Test.java @@ -37,9 +37,10 @@ public class Issue2221Test { public void writeDuplicateHeaderAliasTest() { final ExcelWriter writer = ExcelUtil.getWriter("d:/test/duplicateAlias.xlsx"); // 设置别名 - writer.addHeaderAlias("androidLc", "安卓"); - writer.addHeaderAlias("androidAc", "安卓"); - writer.setOnlyAlias(true); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("androidLc", "安卓"); + config.addHeaderAlias("androidAc", "安卓"); + config.setOnlyAlias(true); // 写入数据 final List> data = ListUtil.view( @@ -77,12 +78,13 @@ public class Issue2221Test { writer.setFreezePane(2); // 设置别名 - writer.addHeaderAlias("date", "日期"); - writer.addHeaderAlias("androidLc", "安卓"); - writer.addHeaderAlias("iosLc", "iOS"); - writer.addHeaderAlias("androidAc", " 安卓"); - writer.addHeaderAlias("iosAc", " iOS"); - writer.setOnlyAlias(true); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("date", "日期"); + config.addHeaderAlias("androidLc", "安卓"); + config.addHeaderAlias("iosLc", "iOS"); + config.addHeaderAlias("androidAc", " 安卓"); + config.addHeaderAlias("iosAc", " iOS"); + config.setOnlyAlias(true); // 设置合并的单元格 writer.merge(new CellRangeAddress(0, 1, 0, 0), "日期", true); diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/IssueI66Z6BTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/IssueI66Z6BTest.java index 7c69b5435..60efad165 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/IssueI66Z6BTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/IssueI66Z6BTest.java @@ -57,8 +57,9 @@ public class IssueI66Z6BTest { final ExcelWriter writer = ExcelUtil.getWriter(destFile); //自定义标题别名 - writer.addHeaderAlias("姓名", "name"); - writer.addHeaderAlias("年龄", "age"); + final ExcelWriteConfig config = writer.getConfig(); + config.addHeaderAlias("姓名", "name"); + config.addHeaderAlias("年龄", "age"); writer.write(dataList, true); writer.close();