add ExcelConfig

This commit is contained in:
Looly 2024-08-10 23:35:34 +08:00
parent 103e48cf06
commit 6440c302a5
19 changed files with 539 additions and 426 deletions

View File

@ -31,18 +31,22 @@ import java.io.Closeable;
import java.io.File; import java.io.File;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Excel基础类用于抽象ExcelWriter和ExcelReader中共用部分的对象和方法 * Excel基础类用于抽象ExcelWriter和ExcelReader中共用部分的对象和方法
* *
* @param <T> 子类类型用于返回this * @param <T> 子类类型用于返回this
* @param <C> ExcelConfig类型
* @author looly * @author looly
* @since 4.1.4 * @since 4.1.4
*/ */
public class ExcelBase<T extends ExcelBase<T>> implements Closeable { public class ExcelBase<T extends ExcelBase<T, C>, C extends ExcelConfig> implements Closeable {
/**
* Excel配置此项不为空
*/
protected C config;
/** /**
* 是否被关闭 * 是否被关闭
*/ */
@ -59,22 +63,40 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
* Excel中对应的Sheet * Excel中对应的Sheet
*/ */
protected Sheet sheet; protected Sheet sheet;
/**
* 标题行别名
*/
protected Map<String, String> headerAlias;
/** /**
* 构造 * 构造
* *
* @param config config
* @param sheet Excel中的sheet * @param sheet Excel中的sheet
*/ */
public ExcelBase(final Sheet sheet) { public ExcelBase(final C config, final Sheet sheet) {
Assert.notNull(sheet, "No Sheet provided."); this.config = Assert.notNull(config);
this.sheet = sheet; this.sheet = Assert.notNull(sheet, "No Sheet provided.");
this.workbook = sheet.getWorkbook(); 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 * 获取Workbook
* *
@ -428,24 +450,26 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
/** /**
* 创建 {@link Hyperlink}默认内容标签为链接地址本身 * 创建 {@link Hyperlink}默认内容标签为链接地址本身
* @param type 链接类型 *
* @param type 链接类型
* @param address 链接地址 * @param address 链接地址
* @return 链接 * @return 链接
* @since 5.7.13 * @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); return createHyperlink(type, address, address);
} }
/** /**
* 创建 {@link Hyperlink}默认内容 * 创建 {@link Hyperlink}默认内容
* @param type 链接类型 *
* @param type 链接类型
* @param address 链接地址 * @param address 链接地址
* @param label 标签即单元格中显示的内容 * @param label 标签即单元格中显示的内容
* @return 链接 * @return 链接
* @since 5.7.13 * @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); final Hyperlink hyperlink = this.workbook.getCreationHelper().createHyperlink(type);
hyperlink.setAddress(address); hyperlink.setAddress(address);
hyperlink.setLabel(label); hyperlink.setLabel(label);
@ -572,66 +596,4 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
this.workbook = null; this.workbook = null;
this.isClosed = true; this.isClosed = true;
} }
/**
* 获得标题行的别名Map
*
* @return 别名Map
*/
public Map<String, String> getHeaderAlias() {
return headerAlias;
}
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
* @return this
*/
@SuppressWarnings("unchecked")
public T setHeaderAlias(final Map<String, String> 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<String, String> 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中的keyvalue为别名
*
* @return this
*/
@SuppressWarnings("unchecked")
public T clearHeaderAlias() {
this.headerAlias = null;
return (T) this;
}
} }

View File

@ -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<String, String> headerAlias;
/**
* 单元格值处理接口
*/
protected CellEditor cellEditor;
/**
* 获得标题行的别名Map
*
* @return 别名Map
*/
public Map<String, String> getHeaderAlias() {
return headerAlias;
}
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
* @return this
*/
public ExcelConfig setHeaderAlias(final Map<String, String> headerAlias) {
this.headerAlias = headerAlias;
return this;
}
/**
* 增加标题别名
*
* @param header 标题
* @param alias 别名
* @return this
*/
public ExcelConfig addHeaderAlias(final String header, final String alias) {
Map<String, String> 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中的keyvalue为别名
*
* @return this
*/
public ExcelConfig clearHeaderAlias() {
return setHeaderAlias(null);
}
/**
* 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header
*
* @param headerList 原标题列表
* @return 转换别名列表
*/
public List<String> aliasHeader(final List<Object> headerList) {
if (CollUtil.isEmpty(headerList)) {
return new ArrayList<>(0);
}
final int size = headerList.size();
final List<String> 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;
}
/**
* 设置单元格值处理逻辑<br>
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等
*
* @param cellEditor 单元格值处理接口
* @return this
*/
public ExcelConfig setCellEditor(final CellEditor cellEditor) {
this.cellEditor = cellEditor;
return this;
}
}

View File

@ -73,7 +73,7 @@ public class RowUtil {
*/ */
public static List<Object> readRow(final Row row, final int startCellNumInclude, final int endCellNumInclude, final CellEditor cellEditor) { public static List<Object> readRow(final Row row, final int startCellNumInclude, final int endCellNumInclude, final CellEditor cellEditor) {
if (null == row) { if (null == row) {
return new ArrayList<>(0); return ListUtil.empty();
} }
final short rowLength = row.getLastCellNum(); final short rowLength = row.getLastCellNum();
if (rowLength < 0) { if (rowLength < 0) {

View File

@ -13,16 +13,8 @@
package org.dromara.hutool.poi.excel.reader; package org.dromara.hutool.poi.excel.reader;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.dromara.hutool.core.collection.CollUtil; import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.poi.excel.ExcelConfig;
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;
/** /**
* 抽象{@link Sheet}数据读取实现 * 抽象{@link Sheet}数据读取实现
@ -33,26 +25,15 @@ import java.util.Map;
*/ */
public abstract class AbstractSheetReader<T> implements SheetReader<T> { public abstract class AbstractSheetReader<T> implements SheetReader<T> {
/** protected final CellRangeAddress cellRangeAddress;
* 读取起始行包含从0开始计数
*/
protected final int startRowIndex;
/**
* 读取结束行包含从0开始计数
*/
protected final int endRowIndex;
/** /**
* 是否忽略空行 * 是否忽略空行
*/ */
protected boolean ignoreEmptyRow = true; protected boolean ignoreEmptyRow = true;
/** /**
* 单元格值处理接口 * Excel配置
*/ */
protected CellEditor cellEditor; protected ExcelConfig config;
/**
* 标题别名
*/
private Map<String, String> headerAlias;
/** /**
* 构造 * 构造
@ -61,18 +42,28 @@ public abstract class AbstractSheetReader<T> implements SheetReader<T> {
* @param endRowIndex 结束行包含从0开始计数 * @param endRowIndex 结束行包含从0开始计数
*/ */
public AbstractSheetReader(final int startRowIndex, final int endRowIndex) { public AbstractSheetReader(final int startRowIndex, final int endRowIndex) {
this.startRowIndex = startRowIndex; this(new CellRangeAddress(
this.endRowIndex = endRowIndex; Math.min(startRowIndex, endRowIndex),
Math.max(startRowIndex, endRowIndex),
0, Integer.MAX_VALUE));
} }
/** /**
* 设置单元格值处理逻辑<br> * 构造
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等
* *
* @param cellEditor 单元格值处理接口 * @param cellRangeAddress 读取范围
*/ */
public void setCellEditor(final CellEditor cellEditor) { public AbstractSheetReader(final CellRangeAddress cellRangeAddress) {
this.cellEditor = cellEditor; this.cellRangeAddress = cellRangeAddress;
}
/**
* 设置Excel配置
*
* @param config Excel配置
*/
public void setExcelConfig(final ExcelConfig config) {
this.config = config;
} }
/** /**
@ -83,78 +74,4 @@ public abstract class AbstractSheetReader<T> implements SheetReader<T> {
public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) { public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) {
this.ignoreEmptyRow = ignoreEmptyRow; this.ignoreEmptyRow = ignoreEmptyRow;
} }
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
*/
public void setHeaderAlias(final Map<String, String> headerAlias) {
this.headerAlias = headerAlias;
}
/**
* 增加标题别名
*
* @param header 标题
* @param alias 别名
*/
public void addHeaderAlias(final String header, final String alias) {
Map<String, String> headerAlias = this.headerAlias;
if (null == headerAlias) {
headerAlias = new LinkedHashMap<>();
}
this.headerAlias = headerAlias;
this.headerAlias.put(header, alias);
}
/**
* 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header
*
* @param headerList 原标题列表
* @return 转换别名列表
*/
protected List<String> aliasHeader(final List<Object> headerList) {
if (CollUtil.isEmpty(headerList)) {
return new ArrayList<>(0);
}
final int size = headerList.size();
final ArrayList<String> 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<Object> readRow(final Sheet sheet, final int rowIndex) {
return RowUtil.readRow(sheet.getRow(rowIndex), this.cellEditor);
}
} }

View File

@ -12,10 +12,10 @@
package org.dromara.hutool.poi.excel.reader; 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.BeanUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions; import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.poi.excel.cell.CellEditor; import org.dromara.hutool.poi.excel.ExcelConfig;
import org.apache.poi.ss.usermodel.Sheet;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -63,13 +63,12 @@ public class BeanSheetReader<T> implements SheetReader<List<T>> {
} }
/** /**
* 设置单元格值处理逻辑<br> * 设置Excel配置
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等
* *
* @param cellEditor 单元格值处理接口 * @param config Excel配置
*/ */
public void setCellEditor(final CellEditor cellEditor) { public void setExcelConfig(final ExcelConfig config) {
this.mapSheetReader.setCellEditor(cellEditor); this.mapSheetReader.setExcelConfig(config);
} }
/** /**
@ -80,23 +79,4 @@ public class BeanSheetReader<T> implements SheetReader<List<T>> {
public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) { public void setIgnoreEmptyRow(final boolean ignoreEmptyRow) {
this.mapSheetReader.setIgnoreEmptyRow(ignoreEmptyRow); this.mapSheetReader.setIgnoreEmptyRow(ignoreEmptyRow);
} }
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
*/
public void setHeaderAlias(final Map<String, String> headerAlias) {
this.mapSheetReader.setHeaderAlias(headerAlias);
}
/**
* 增加标题别名
*
* @param header 标题
* @param alias 别名
*/
public void addHeaderAlias(final String header, final String alias) {
this.mapSheetReader.addHeaderAlias(header, alias);
}
} }

View File

@ -12,8 +12,10 @@
package org.dromara.hutool.poi.excel.reader; 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.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.ArrayList;
import java.util.List; import java.util.List;
@ -26,8 +28,6 @@ import java.util.List;
*/ */
public class ColumnSheetReader extends AbstractSheetReader<List<Object>> { public class ColumnSheetReader extends AbstractSheetReader<List<Object>> {
private final int columnIndex;
/** /**
* 构造 * 构造
* *
@ -36,17 +36,18 @@ public class ColumnSheetReader extends AbstractSheetReader<List<Object>> {
* @param endRowIndex 结束行包含从0开始计数 * @param endRowIndex 结束行包含从0开始计数
*/ */
public ColumnSheetReader(final int columnIndex, final int startRowIndex, final int endRowIndex) { public ColumnSheetReader(final int columnIndex, final int startRowIndex, final int endRowIndex) {
super(startRowIndex, endRowIndex); super(new CellRangeAddress(startRowIndex, endRowIndex, columnIndex, columnIndex));
this.columnIndex = columnIndex;
} }
@Override @Override
public List<Object> read(final Sheet sheet) { public List<Object> read(final Sheet sheet) {
final List<Object> resultList = new ArrayList<>(); final List<Object> resultList = new ArrayList<>();
final int startRowIndex = Math.max(this.startRowIndex, sheet.getFirstRowNum());// 读取起始行包含 final int startRowIndex = Math.max(this.cellRangeAddress.getFirstRow(), sheet.getFirstRowNum());// 读取起始行包含
final int endRowIndex = Math.min(this.endRowIndex, sheet.getLastRowNum());// 读取结束行包含 final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), sheet.getLastRowNum());// 读取结束行包含
final int columnIndex = this.cellRangeAddress.getFirstColumn();
final CellEditor cellEditor = this.config.getCellEditor();
Object value; Object value;
for (int i = startRowIndex; i <= endRowIndex; i++) { for (int i = startRowIndex; i <= endRowIndex; i++) {
value = CellUtil.getCellValue(CellUtil.getCell(sheet.getRow(i), columnIndex), cellEditor); value = CellUtil.getCellValue(CellUtil.getCell(sheet.getRow(i), columnIndex), cellEditor);

View File

@ -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<Void> {
private final SerBiConsumer<Cell, Object> cellHandler;
/**
* 构造
*
* @param startRowIndex 起始行包含从0开始计数
* @param endRowIndex 结束行包含从0开始计数
* @param cellHandler 单元格处理器用于处理读到的单元格及其数据
*/
public ConsumerSheetReader(final int startRowIndex, final int endRowIndex, final SerBiConsumer<Cell, Object> 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;
}
}

View File

@ -12,18 +12,17 @@
package org.dromara.hutool.poi.excel.reader; 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.extractor.ExcelExtractor;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook; 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 org.dromara.hutool.poi.excel.writer.ExcelWriter;
import java.io.File; import java.io.File;
@ -38,16 +37,12 @@ import java.util.Map;
* @author Looly * @author Looly
* @since 3.1.0 * @since 3.1.0
*/ */
public class ExcelReader extends ExcelBase<ExcelReader> { public class ExcelReader extends ExcelBase<ExcelReader, ExcelConfig> {
/** /**
* 是否忽略空行 * 是否忽略空行
*/ */
private boolean ignoreEmptyRow = true; private boolean ignoreEmptyRow = true;
/**
* 单元格值处理接口
*/
private CellEditor cellEditor;
// ------------------------------------------------------------------------------------------------------- Constructor start // ------------------------------------------------------------------------------------------------------- Constructor start
/** /**
@ -139,7 +134,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* @param sheet Excel中的sheet * @param sheet Excel中的sheet
*/ */
public ExcelReader(final Sheet sheet) { public ExcelReader(final Sheet sheet) {
super(sheet); super(new ExcelConfig(), sheet);
} }
// ------------------------------------------------------------------------------------------------------- Constructor end // ------------------------------------------------------------------------------------------------------- Constructor end
@ -165,17 +160,6 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
return this; return this;
} }
/**
* 设置单元格值处理逻辑<br>
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等
*
* @param cellEditor 单元格值处理接口
* @return this
*/
public ExcelReader setCellEditor(final CellEditor cellEditor) {
this.cellEditor = cellEditor;
return this;
}
// ------------------------------------------------------------------------------------------------------- Getters and Setters end // ------------------------------------------------------------------------------------------------------- Getters and Setters end
/** /**
@ -220,9 +204,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
*/ */
public List<List<Object>> read(final int startRowIndex, final int endRowIndex, final boolean aliasFirstLine) { public List<List<Object>> read(final int startRowIndex, final int endRowIndex, final boolean aliasFirstLine) {
final ListSheetReader reader = new ListSheetReader(startRowIndex, endRowIndex, aliasFirstLine); final ListSheetReader reader = new ListSheetReader(startRowIndex, endRowIndex, aliasFirstLine);
reader.setCellEditor(this.cellEditor); reader.setExcelConfig(this.config);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow); reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader); return read(reader);
} }
@ -249,9 +232,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
*/ */
public List<Object> readColumn(final int columnIndex, final int startRowIndex, final int endRowIndex) { public List<Object> readColumn(final int columnIndex, final int startRowIndex, final int endRowIndex) {
final ColumnSheetReader reader = new ColumnSheetReader(columnIndex, startRowIndex, endRowIndex); final ColumnSheetReader reader = new ColumnSheetReader(columnIndex, startRowIndex, endRowIndex);
reader.setCellEditor(this.cellEditor); reader.setExcelConfig(this.config);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow); reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader); return read(reader);
} }
@ -275,25 +257,13 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* @param cellHandler 单元格处理器用于处理读到的单元格及其数据 * @param cellHandler 单元格处理器用于处理读到的单元格及其数据
* @since 5.3.8 * @since 5.3.8
*/ */
public void read(int startRowIndex, int endRowIndex, final SerBiConsumer<Cell, Object> cellHandler) { public void read(final int startRowIndex, final int endRowIndex, final SerBiConsumer<Cell, Object> cellHandler) {
checkNotClosed(); checkNotClosed();
startRowIndex = Math.max(startRowIndex, this.sheet.getFirstRowNum());// 读取起始行包含 final ConsumerSheetReader reader = new ConsumerSheetReader(startRowIndex, endRowIndex, cellHandler);
endRowIndex = Math.min(endRowIndex, this.sheet.getLastRowNum());// 读取结束行包含 reader.setExcelConfig(this.config);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
Row row; reader.read(sheet);
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));
}
}
}
} }
/** /**
@ -317,9 +287,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
*/ */
public List<Map<String, Object>> read(final int headerRowIndex, final int startRowIndex, final int endRowIndex) { public List<Map<String, Object>> read(final int headerRowIndex, final int startRowIndex, final int endRowIndex) {
final MapSheetReader reader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex); final MapSheetReader reader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex);
reader.setCellEditor(this.cellEditor); reader.setExcelConfig(this.config);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow); reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader); return read(reader);
} }
@ -360,9 +329,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
*/ */
public <T> List<T> read(final int headerRowIndex, final int startRowIndex, final int endRowIndex, final Class<T> beanType) { public <T> List<T> read(final int headerRowIndex, final int startRowIndex, final int endRowIndex, final Class<T> beanType) {
final BeanSheetReader<T> reader = new BeanSheetReader<>(headerRowIndex, startRowIndex, endRowIndex, beanType); final BeanSheetReader<T> reader = new BeanSheetReader<>(headerRowIndex, startRowIndex, endRowIndex, beanType);
reader.setCellEditor(this.cellEditor); reader.setExcelConfig(this.config);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow); reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader); return read(reader);
} }
@ -421,7 +389,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* @since 4.0.3 * @since 4.0.3
*/ */
public Object readCellValue(final int x, final int y) { 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<ExcelReader> {
* @return 单元格值列表 * @return 单元格值列表
*/ */
private List<Object> readRow(final Row row) { private List<Object> readRow(final Row row) {
return RowUtil.readRow(row, this.cellEditor); return RowUtil.readRow(row, this.config.getCellEditor());
} }
/** /**

View File

@ -15,6 +15,8 @@ package org.dromara.hutool.poi.excel.reader;
import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.convert.Convert; import org.dromara.hutool.core.convert.Convert;
import org.apache.poi.ss.usermodel.Sheet; 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.ArrayList;
import java.util.List; import java.util.List;
@ -46,15 +48,17 @@ public class ListSheetReader extends AbstractSheetReader<List<List<Object>>> {
public List<List<Object>> read(final Sheet sheet) { public List<List<Object>> read(final Sheet sheet) {
final List<List<Object>> resultList = new ArrayList<>(); final List<List<Object>> resultList = new ArrayList<>();
final int startRowIndex = Math.max(this.startRowIndex, sheet.getFirstRowNum());// 读取起始行包含 final int startRowIndex = Math.max(this.cellRangeAddress.getFirstRow(), sheet.getFirstRowNum());// 读取起始行包含
final int endRowIndex = Math.min(this.endRowIndex, sheet.getLastRowNum());// 读取结束行包含 final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), sheet.getLastRowNum());// 读取结束行包含
List<Object> rowList; List<Object> rowList;
final CellEditor cellEditor = this.config.getCellEditor();
for (int i = startRowIndex; i <= endRowIndex; i++) { for (int i = startRowIndex; i <= endRowIndex; i++) {
rowList = readRow(sheet, i); rowList = RowUtil.readRow(sheet.getRow(i), cellEditor);
if (CollUtil.isNotEmpty(rowList) || !ignoreEmptyRow) { if (CollUtil.isNotEmpty(rowList) || !ignoreEmptyRow) {
if (aliasFirstLine && i == startRowIndex) { if (aliasFirstLine && i == startRowIndex) {
// 第一行作为标题行替换别名 // 第一行作为标题行替换别名
rowList = Convert.toList(Object.class, aliasHeader(rowList)); rowList = Convert.toList(Object.class, this.config.aliasHeader(rowList));
} }
resultList.add(rowList); resultList.add(rowList);
} }

View File

@ -17,6 +17,7 @@ import org.dromara.hutool.core.collection.iter.IterUtil;
import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
import org.dromara.hutool.poi.excel.RowUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -57,15 +58,18 @@ public class MapSheetReader extends AbstractSheetReader<List<Map<String, Object>
throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is lower than first row index {}.", headerRowIndex, firstRowNum)); throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is lower than first row index {}.", headerRowIndex, firstRowNum));
} else if (headerRowIndex > lastRowNum) { } else if (headerRowIndex > lastRowNum) {
throw new IndexOutOfBoundsException(StrUtil.format("Header row index {} is greater than last row index {}.", 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 // issue#I5U1JA 只有标题行的Excel起始行是1标题行最后的行号是0
return ListUtil.empty(); return ListUtil.empty();
} }
final int startRowIndex = Math.max(this.startRowIndex, firstRowNum);// 读取起始行包含 startRowIndex = Math.max(startRowIndex, firstRowNum);// 读取起始行包含
final int endRowIndex = Math.min(this.endRowIndex, lastRowNum);// 读取结束行包含 final int endRowIndex = Math.min(this.cellRangeAddress.getLastRow(), lastRowNum);// 读取结束行包含
// 读取header // 读取header
final List<String> headerList = aliasHeader(readRow(sheet, headerRowIndex)); final List<String> headerList = this.config.aliasHeader(readRow(sheet, headerRowIndex));
final List<Map<String, Object>> result = new ArrayList<>(endRowIndex - startRowIndex + 1); final List<Map<String, Object>> result = new ArrayList<>(endRowIndex - startRowIndex + 1);
List<Object> rowList; List<Object> rowList;
@ -80,4 +84,15 @@ public class MapSheetReader extends AbstractSheetReader<List<Map<String, Object>
} }
return result; return result;
} }
/**
* 读取某一行数据
*
* @param sheet {@link Sheet}
* @param rowIndex 行号从0开始
* @return 一行数据
*/
private List<Object> readRow(final Sheet sheet, final int rowIndex) {
return RowUtil.readRow(sheet.getRow(rowIndex), this.config.getCellEditor());
}
} }

View File

@ -12,6 +12,7 @@
package org.dromara.hutool.poi.excel.writer; 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.file.FileUtil;
import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IORuntimeException;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
@ -32,6 +33,9 @@ import java.io.OutputStream;
*/ */
public class BigExcelWriter extends ExcelWriter { public class BigExcelWriter extends ExcelWriter {
/**
* 默认内存中保存的行数默认100
*/
public static final int DEFAULT_WINDOW_SIZE = SXSSFWorkbook.DEFAULT_WINDOW_SIZE; public static final int DEFAULT_WINDOW_SIZE = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;
/** /**
@ -181,6 +185,7 @@ public class BigExcelWriter extends ExcelWriter {
return this; return this;
} }
@SuppressWarnings("resource")
@Override @Override
public void close() { public void close() {
if (null != this.destFile && !isFlushed) { if (null != this.destFile && !isFlushed) {
@ -188,7 +193,7 @@ public class BigExcelWriter extends ExcelWriter {
} }
// 清理临时文件 // 清理临时文件
((SXSSFWorkbook) this.workbook).dispose(); IoUtil.closeIfPossible(this.workbook);
super.closeWithoutFlush(); super.closeWithoutFlush();
} }
} }

View File

@ -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<String> aliasComparator;
@Override
public ExcelWriteConfig setHeaderAlias(final Map<String, String> 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<String> getCachedAliasComparator() {
final Map<String, String> headerAlias = this.headerAlias;
if (MapUtil.isEmpty(headerAlias)) {
return null;
}
Comparator<String> aliasComparator = this.aliasComparator;
if (null == aliasComparator) {
final Set<String> keySet = headerAlias.keySet();
aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0]));
this.aliasComparator = aliasComparator;
}
return aliasComparator;
}
}

View File

@ -22,7 +22,6 @@ import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.collection.ListUtil; 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.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.io.file.FileUtil;
@ -63,16 +62,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* @since 3.2.0 * @since 3.2.0
*/ */
@SuppressWarnings("resource") @SuppressWarnings("resource")
public class ExcelWriter extends ExcelBase<ExcelWriter> { public class ExcelWriter extends ExcelBase<ExcelWriter, ExcelWriteConfig> {
/**
* 是否只保留别名对应的字段
*/
private boolean onlyAlias;
/**
* 标题顺序比较器
*/
private Comparator<String> aliasComparator;
/** /**
* 样式集定义不同类型数据样式 * 样式集定义不同类型数据样式
*/ */
@ -81,10 +72,6 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
* 标题项对应列号缓存每次写标题更新此缓存 * 标题项对应列号缓存每次写标题更新此缓存
*/ */
private Map<String, Integer> headLocationCache; private Map<String, Integer> headLocationCache;
/**
* 单元格值处理接口
*/
private CellEditor cellEditor;
/** /**
* 当前行 * 当前行
*/ */
@ -188,22 +175,16 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
* @since 4.0.6 * @since 4.0.6
*/ */
public ExcelWriter(final Sheet sheet) { public ExcelWriter(final Sheet sheet) {
super(sheet); super(new ExcelWriteConfig(), sheet);
this.styleSet = new DefaultStyleSet(workbook); this.styleSet = new DefaultStyleSet(workbook);
this.currentRow = new AtomicInteger(0); this.currentRow = new AtomicInteger(0);
} }
// endregion // endregion
/**
* 设置单元格值处理逻辑<br> @Override
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等 public ExcelWriter setConfig(final ExcelWriteConfig config) {
* return super.setConfig(config);
* @param cellEditor 单元格值处理接口
* @return this
*/
public ExcelWriter setCellEditor(final CellEditor cellEditor) {
this.cellEditor = cellEditor;
return this;
} }
@Override @Override
@ -410,39 +391,6 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
} }
//region header alias //region header alias
@Override
public ExcelWriter setHeaderAlias(final Map<String, String> 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 //endregion
/** /**
@ -704,7 +652,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
// 设置内容 // 设置内容
if (null != content) { if (null != content) {
final Cell cell = getOrCreateCell(cellRangeAddress.getFirstColumn(), cellRangeAddress.getFirstRow()); 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; return this;
} }
@ -911,11 +859,12 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); Assert.isFalse(this.isClosed, "ExcelWriter has been closed!");
this.headLocationCache = new SafeConcurrentHashMap<>(); this.headLocationCache = new SafeConcurrentHashMap<>();
final Row row = this.sheet.createRow(this.currentRow.getAndIncrement()); final Row row = this.sheet.createRow(this.currentRow.getAndIncrement());
final CellEditor cellEditor = this.config.getCellEditor();
int i = 0; int i = 0;
Cell cell; Cell cell;
for (final Object value : rowData) { for (final Object value : rowData) {
cell = row.createCell(i); 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); this.headLocationCache.put(StrUtil.toString(value), i);
i++; i++;
} }
@ -939,6 +888,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
final Iterator<?> iterator = rowData.iterator(); final Iterator<?> iterator = rowData.iterator();
//如果获取的row存在单元格则执行复杂表头逻辑否则直接调用writeHeadRow(Iterable<?> rowData) //如果获取的row存在单元格则执行复杂表头逻辑否则直接调用writeHeadRow(Iterable<?> rowData)
if (row.getLastCellNum() != 0) { if (row.getLastCellNum() != 0) {
final CellEditor cellEditor = this.config.getCellEditor();
for (int i = 0; i < this.workbook.getSpreadsheetVersion().getMaxColumns(); i++) { for (int i = 0; i < this.workbook.getSpreadsheetVersion().getMaxColumns(); i++) {
Cell cell = row.getCell(i); Cell cell = row.getCell(i);
if (cell != null) { if (cell != null) {
@ -946,7 +896,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
} }
if (iterator.hasNext()) { if (iterator.hasNext()) {
cell = row.createCell(i); cell = row.createCell(i);
CellUtil.setCellValue(cell, iterator.next(), this.styleSet, true, this.cellEditor); CellUtil.setCellValue(cell, iterator.next(), this.styleSet, true, cellEditor);
} else { } else {
break; break;
} }
@ -975,10 +925,12 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
*/ */
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public ExcelWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) { public ExcelWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) {
final ExcelWriteConfig config = this.config;
final Map rowMap; final Map rowMap;
if (rowBean instanceof Map) { if (rowBean instanceof Map) {
if (MapUtil.isNotEmpty(this.headerAlias)) { if (MapUtil.isNotEmpty(config.getHeaderAlias())) {
rowMap = MapUtil.newTreeMap((Map) rowBean, getCachedAliasComparator()); rowMap = MapUtil.newTreeMap((Map) rowBean, config.getCachedAliasComparator());
} else { } else {
rowMap = (Map) rowBean; rowMap = (Map) rowBean;
} }
@ -990,11 +942,11 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
// Hyperlink当成一个值 // Hyperlink当成一个值
return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead);
} else if (BeanUtil.isReadableBean(rowBean.getClass())) { } else if (BeanUtil.isReadableBean(rowBean.getClass())) {
if (MapUtil.isEmpty(this.headerAlias)) { if (MapUtil.isEmpty(config.getHeaderAlias())) {
rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false);
} else { } else {
// 别名存在情况下按照别名的添加顺序排序Bean数据 // 别名存在情况下按照别名的添加顺序排序Bean数据
rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(getCachedAliasComparator()), false, false); rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(config.getCachedAliasComparator()), false, false);
} }
} else { } else {
// 其它转为字符串默认输出 // 其它转为字符串默认输出
@ -1033,6 +985,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
// 如果已经写出标题行根据标题行找对应的值写入 // 如果已经写出标题行根据标题行找对应的值写入
if (MapUtil.isNotEmpty(this.headLocationCache)) { if (MapUtil.isNotEmpty(this.headLocationCache)) {
final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement());
final CellEditor cellEditor = this.config.getCellEditor();
Integer location; Integer location;
for (final Table.Cell<?, ?, ?> cell : aliasTable) { for (final Table.Cell<?, ?, ?> cell : aliasTable) {
// 首先查找原名对应的列号 // 首先查找原名对应的列号
@ -1042,7 +995,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey())); location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey()));
} }
if (null != location) { 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 { } else {
@ -1061,7 +1014,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
*/ */
public ExcelWriter writeRow(final Iterable<?> rowData) { public ExcelWriter writeRow(final Iterable<?> rowData) {
Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); 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; return this;
} }
// endregion // endregion
@ -1185,7 +1138,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
*/ */
public ExcelWriter writeCellValue(final int x, final int y, final Object value, final boolean isHeader) { public ExcelWriter writeCellValue(final int x, final int y, final Object value, final boolean isHeader) {
final Cell cell = getOrCreateCell(x, y); 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; return this;
} }
// endregion // endregion
@ -1406,15 +1359,17 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
*/ */
private Table<?, ?, ?> aliasTable(final Map<?, ?> rowMap) { private Table<?, ?, ?> aliasTable(final Map<?, ?> rowMap) {
final Table<Object, Object, Object> filteredTable = new RowKeyTable<>(new LinkedHashMap<>(), TableMap::new); final Table<Object, Object, Object> filteredTable = new RowKeyTable<>(new LinkedHashMap<>(), TableMap::new);
if (MapUtil.isEmpty(this.headerAlias)) { final Map<String, String> headerAlias = this.config.getHeaderAlias();
final boolean onlyAlias = this.config.onlyAlias;
if (MapUtil.isEmpty(headerAlias)) {
rowMap.forEach((key, value) -> filteredTable.put(key, key, value)); rowMap.forEach((key, value) -> filteredTable.put(key, key, value));
} else { } else {
rowMap.forEach((key, value) -> { rowMap.forEach((key, value) -> {
final String aliasName = this.headerAlias.get(StrUtil.toString(key)); final String aliasName = headerAlias.get(StrUtil.toString(key));
if (null != aliasName) { if (null != aliasName) {
// 别名键值对加入 // 别名键值对加入
filteredTable.put(key, aliasName, value); filteredTable.put(key, aliasName, value);
} else if (!this.onlyAlias) { } else if (!onlyAlias) {
// 保留无别名设置的键值对 // 保留无别名设置的键值对
filteredTable.put(key, key, value); filteredTable.put(key, key, value);
} }
@ -1423,24 +1378,5 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
return filteredTable; return filteredTable;
} }
/**
* 获取单例的别名比较器比较器的顺序为别名加入的顺序
*
* @return Comparator
* @since 4.1.5
*/
private Comparator<String> getCachedAliasComparator() {
if (MapUtil.isEmpty(this.headerAlias)) {
return null;
}
Comparator<String> aliasComparator = this.aliasComparator;
if (null == aliasComparator) {
final Set<String> keySet = this.headerAlias.keySet();
aliasComparator = new IndexedComparator<>(keySet.toArray(new String[0]));
this.aliasComparator = aliasComparator;
}
return aliasComparator;
}
// endregion // endregion
} }

View File

@ -27,7 +27,7 @@ public class CellEditorTest {
@org.junit.jupiter.api.Test @org.junit.jupiter.api.Test
public void readTest(){ public void readTest(){
final ExcelReader excelReader= ExcelUtil.getReader("cell_editor_test.xlsx"); final ExcelReader excelReader= ExcelUtil.getReader("cell_editor_test.xlsx");
excelReader.setCellEditor(new ExcelHandler()); excelReader.getConfig().setCellEditor(new ExcelHandler());
final List<Test> excelReaderObjects=excelReader.readAll(Test.class); final List<Test> excelReaderObjects=excelReader.readAll(Test.class);
Assertions.assertEquals("0", excelReaderObjects.get(0).getTest1()); Assertions.assertEquals("0", excelReaderObjects.get(0).getTest1());

View File

@ -19,6 +19,7 @@ import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.core.util.ObjUtil;
import lombok.Data; import lombok.Data;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.dromara.hutool.poi.excel.ExcelConfig;
import org.dromara.hutool.poi.excel.ExcelUtil; import org.dromara.hutool.poi.excel.ExcelUtil;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -48,7 +49,7 @@ public class ExcelReadTest {
headerAlias.put("库房", "storageName"); headerAlias.put("库房", "storageName");
headerAlias.put("盘点权限", "checkPerm"); headerAlias.put("盘点权限", "checkPerm");
headerAlias.put("领料审批权限", "allotAuditPerm"); headerAlias.put("领料审批权限", "allotAuditPerm");
reader.setHeaderAlias(headerAlias); reader.getConfig().setHeaderAlias(headerAlias);
// 读取list时默认首个非空行为标题 // 读取list时默认首个非空行为标题
final List<List<Object>> read = reader.read(0, Integer.MAX_VALUE, true); final List<List<Object>> read = reader.read(0, Integer.MAX_VALUE, true);
@ -146,10 +147,11 @@ public class ExcelReadTest {
@Test @Test
public void excelReadToBeanListTest() { public void excelReadToBeanListTest() {
final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("aaa.xlsx")); final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("aaa.xlsx"));
reader.addHeaderAlias("姓名", "name"); final ExcelConfig config = reader.getConfig();
reader.addHeaderAlias("年龄", "age"); config.addHeaderAlias("姓名", "name");
reader.addHeaderAlias("性别", "gender"); config.addHeaderAlias("年龄", "age");
reader.addHeaderAlias("鞋码", "shoeSize"); config.addHeaderAlias("性别", "gender");
config.addHeaderAlias("鞋码", "shoeSize");
final List<Person> all = reader.readAll(Person.class); final List<Person> all = reader.readAll(Person.class);
Assertions.assertEquals("张三", all.get(0).getName()); Assertions.assertEquals("张三", all.get(0).getName());
@ -162,9 +164,10 @@ public class ExcelReadTest {
@Disabled @Disabled
public void excelReadToBeanListTest2() { public void excelReadToBeanListTest2() {
final ExcelReader reader = ExcelUtil.getReader("f:/test/toBean.xlsx"); final ExcelReader reader = ExcelUtil.getReader("f:/test/toBean.xlsx");
reader.addHeaderAlias("姓名", "name"); final ExcelConfig config = reader.getConfig();
reader.addHeaderAlias("年龄", "age"); config.addHeaderAlias("姓名", "name");
reader.addHeaderAlias("性别", "gender"); config.addHeaderAlias("年龄", "age");
config.addHeaderAlias("性别", "gender");
final List<Person> all = reader.read(0, 2, Person.class); final List<Person> all = reader.read(0, 2, Person.class);
for (final Person person : all) { for (final Person person : all) {
@ -220,7 +223,8 @@ public class ExcelReadTest {
@Test @Test
public void nullValueEditTest(){ public void nullValueEditTest(){
final ExcelReader reader = ExcelUtil.getReader("null_cell_test.xlsx"); 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<List<Object>> read = reader.read(); final List<List<Object>> read = reader.read();
// 对于任意一个单元格有值的情况下之前的单元格值按照null处理 // 对于任意一个单元格有值的情况下之前的单元格值按照null处理
@ -271,14 +275,14 @@ public class ExcelReadTest {
//https://gitee.com/dromara/hutool/issues/I5OSFC //https://gitee.com/dromara/hutool/issues/I5OSFC
final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("read.xlsx")); final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("read.xlsx"));
final List<Map<String, Object>> read = reader.read(1,2,2); final List<Map<String, Object>> read = reader.read(1,2,2);
for (Map<String, Object> map : read) { for (final Map<String, Object> map : read) {
Console.log(map); Console.log(map);
} }
//超出lastIndex 抛出相应提示startRowIndex row index 4 is greater than last row index 2. //超出lastIndex 抛出相应提示startRowIndex row index 4 is greater than last row index 2.
//而非:Illegal Capacity: -1 //而非:Illegal Capacity: -1
try { try {
final List<Map<String, Object>> readGreaterIndex = reader.read(1,4,4); final List<Map<String, Object>> readGreaterIndex = reader.read(1,4,4);
} catch (Exception e) { } catch (final Exception e) {
Console.log(e.toString()); Console.log(e.toString());
} }
} }

View File

@ -191,11 +191,12 @@ public class BigExcelWriteTest {
FileUtil.del(FileUtil.file(file)); FileUtil.del(FileUtil.file(file));
final BigExcelWriter writer = ExcelUtil.getBigWriter(file); final BigExcelWriter writer = ExcelUtil.getBigWriter(file);
//自定义标题 //自定义标题
writer.addHeaderAlias("name", "姓名"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("age", "年龄"); config.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("score", "分数"); config.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("isPass", "是否通过"); config.addHeaderAlias("score", "分数");
writer.addHeaderAlias("examDate", "考试时间"); config.addHeaderAlias("isPass", "是否通过");
config.addHeaderAlias("examDate", "考试时间");
// 合并单元格后的标题行使用默认标题样式 // 合并单元格后的标题行使用默认标题样式
writer.merge(4, "一班成绩单"); writer.merge(4, "一班成绩单");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
@ -234,8 +235,9 @@ public class BigExcelWriteTest {
final String path = "d:/test/issue1210.xlsx"; final String path = "d:/test/issue1210.xlsx";
FileUtil.del(FileUtil.file(path)); FileUtil.del(FileUtil.file(path));
final BigExcelWriter writer = ExcelUtil.getBigWriter(path); final BigExcelWriter writer = ExcelUtil.getBigWriter(path);
writer.addHeaderAlias("id", "SN"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("userName", "User Name"); config.addHeaderAlias("id", "SN");
config.addHeaderAlias("userName", "User Name");
final List<Map<String, Object>> list = new ArrayList<>(); final List<Map<String, Object>> list = new ArrayList<>();
list.add(new HashMap<String, Object>() { list.add(new HashMap<String, Object>() {

View File

@ -306,11 +306,12 @@ public class ExcelWriteTest {
FileUtil.del(FileUtil.file(file)); FileUtil.del(FileUtil.file(file));
final ExcelWriter writer = ExcelUtil.getWriter(file); final ExcelWriter writer = ExcelUtil.getWriter(file);
// 自定义标题 // 自定义标题
writer.addHeaderAlias("name", "姓名"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("age", "年龄"); config.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("score", "分数"); config.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("isPass", "是否通过"); config.addHeaderAlias("score", "分数");
writer.addHeaderAlias("examDate", "考试时间"); config.addHeaderAlias("isPass", "是否通过");
config.addHeaderAlias("examDate", "考试时间");
// 合并单元格后的标题行使用默认标题样式 // 合并单元格后的标题行使用默认标题样式
writer.merge(4, "一班成绩单"); writer.merge(4, "一班成绩单");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
@ -340,10 +341,11 @@ public class ExcelWriteTest {
final String file = "f:/test/test_alias.xlsx"; final String file = "f:/test/test_alias.xlsx";
FileUtil.del(FileUtil.file(file)); FileUtil.del(FileUtil.file(file));
final ExcelWriter writer = ExcelUtil.getWriter(file); final ExcelWriter writer = ExcelUtil.getWriter(file);
writer.setOnlyAlias(true); final ExcelWriteConfig config = writer.getConfig();
config.setOnlyAlias(true);
// 自定义标题 // 自定义标题
writer.addHeaderAlias("name", "姓名"); config.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("age", "年龄"); config.addHeaderAlias("age", "年龄");
// 合并单元格后的标题行使用默认标题样式 // 合并单元格后的标题行使用默认标题样式
writer.merge(4, "一班成绩单"); writer.merge(4, "一班成绩单");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
@ -372,10 +374,11 @@ public class ExcelWriteTest {
// 通过工具类创建writer // 通过工具类创建writer
final String file = "d:/test/test_alias.xls"; final String file = "d:/test/test_alias.xls";
final ExcelWriter writer = ExcelUtil.getWriter(file, "test1"); final ExcelWriter writer = ExcelUtil.getWriter(file, "test1");
// writer.setOnlyAlias(true);
// 自定义标题 // 自定义标题
writer.addHeaderAlias("name", "姓名"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("age", "年龄"); // writer.setOnlyAlias(true);
config.addHeaderAlias("name", "姓名");
config.addHeaderAlias("age", "年龄");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
writer.write(rows, true); writer.write(rows, true);
// 关闭writer释放内存 // 关闭writer释放内存
@ -403,12 +406,13 @@ public class ExcelWriteTest {
// 通过工具类创建writer // 通过工具类创建writer
final String file = "d:/test/test_alias.xls"; final String file = "d:/test/test_alias.xls";
final ExcelWriter writer = ExcelUtil.getWriter(file, "test1"); final ExcelWriter writer = ExcelUtil.getWriter(file, "test1");
writer.setOnlyAlias(true); final ExcelWriteConfig config = writer.getConfig();
config.setOnlyAlias(true);
// 自定义标题 // 自定义标题
writer.addHeaderAlias("name", "姓名"); config.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("age", "年龄"); config.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("examDate", "考试时间"); config.addHeaderAlias("examDate", "考试时间");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
writer.write(rows, true); writer.write(rows, true);
@ -438,12 +442,13 @@ public class ExcelWriteTest {
final String file = "e:/writeBeanTest.xlsx"; final String file = "e:/writeBeanTest.xlsx";
FileUtil.del(FileUtil.file(file)); FileUtil.del(FileUtil.file(file));
final ExcelWriter writer = ExcelUtil.getWriter(file); final ExcelWriter writer = ExcelUtil.getWriter(file);
final ExcelWriteConfig config = writer.getConfig();
// 自定义标题 // 自定义标题
writer.addHeaderAlias("name", "姓名"); config.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("age", "年龄"); config.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("score", "分数"); config.addHeaderAlias("score", "分数");
writer.addHeaderAlias("isPass", "是否通过"); config.addHeaderAlias("isPass", "是否通过");
writer.addHeaderAlias("examDate", "考试时间"); config.addHeaderAlias("examDate", "考试时间");
// 合并单元格后的标题行使用默认标题样式 // 合并单元格后的标题行使用默认标题样式
writer.merge(4, "一班成绩单"); writer.merge(4, "一班成绩单");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
@ -471,9 +476,10 @@ public class ExcelWriteTest {
FileUtil.del(FileUtil.file(file)); FileUtil.del(FileUtil.file(file));
final ExcelWriter writer = ExcelUtil.getWriter(file); final ExcelWriter writer = ExcelUtil.getWriter(file);
// 自定义标题 // 自定义标题
writer.addHeaderAlias("id", "编号"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("num", "序号"); config.addHeaderAlias("id", "编号");
writer.addHeaderAlias("body", "内容"); config.addHeaderAlias("num", "序号");
config.addHeaderAlias("body", "内容");
// 一次性写出内容使用默认样式 // 一次性写出内容使用默认样式
writer.write(rows, true); writer.write(rows, true);
// 关闭writer释放内存 // 关闭writer释放内存
@ -529,18 +535,19 @@ public class ExcelWriteTest {
rows.add(tempList); rows.add(tempList);
} }
final ExcelWriter writer = ExcelUtil.getWriter("D:\\test\\multiSheet.xlsx", "正常数据"); final ExcelWriter writer = ExcelUtil.getWriter("D:\\test\\multiSheet.xlsx", "正常数据");
writer.addHeaderAlias("1", "row1"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("3", "row2"); config.addHeaderAlias("1", "row1");
writer.setOnlyAlias(true); config.addHeaderAlias("3", "row2");
config.setOnlyAlias(true);
writer.write(rows, true); writer.write(rows, true);
writer.autoSizeColumnAll(false, 0); writer.autoSizeColumnAll(false, 0);
//表2 //表2
writer.setSheet("当前重复数据"); writer.setSheet("当前重复数据");
writer.clearHeaderAlias(); config.clearHeaderAlias();
writer.addHeaderAlias("3", "行3"); config.addHeaderAlias("3", "行3");
writer.addHeaderAlias("1", "行1"); config.addHeaderAlias("1", "行1");
writer.write(rows, true); writer.write(rows, true);
writer.autoSizeColumnAll(false, 0); writer.autoSizeColumnAll(false, 0);
@ -749,7 +756,7 @@ public class ExcelWriteTest {
//通过工具类创建writer //通过工具类创建writer
FileUtil.del(FileUtil.file("d:/test/writeTest2123.xlsx")); FileUtil.del(FileUtil.file("d:/test/writeTest2123.xlsx"));
final ExcelWriter writer = ExcelUtil.getWriter("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, "测试标题"); writer.merge(7, "测试标题");

View File

@ -37,9 +37,10 @@ public class Issue2221Test {
public void writeDuplicateHeaderAliasTest() { public void writeDuplicateHeaderAliasTest() {
final ExcelWriter writer = ExcelUtil.getWriter("d:/test/duplicateAlias.xlsx"); final ExcelWriter writer = ExcelUtil.getWriter("d:/test/duplicateAlias.xlsx");
// 设置别名 // 设置别名
writer.addHeaderAlias("androidLc", "安卓"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("androidAc", "安卓"); config.addHeaderAlias("androidLc", "安卓");
writer.setOnlyAlias(true); config.addHeaderAlias("androidAc", "安卓");
config.setOnlyAlias(true);
// 写入数据 // 写入数据
final List<Map<Object, Object>> data = ListUtil.view( final List<Map<Object, Object>> data = ListUtil.view(
@ -77,12 +78,13 @@ public class Issue2221Test {
writer.setFreezePane(2); writer.setFreezePane(2);
// 设置别名 // 设置别名
writer.addHeaderAlias("date", "日期"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("androidLc", "安卓"); config.addHeaderAlias("date", "日期");
writer.addHeaderAlias("iosLc", "iOS"); config.addHeaderAlias("androidLc", "安卓");
writer.addHeaderAlias("androidAc", " 安卓"); config.addHeaderAlias("iosLc", "iOS");
writer.addHeaderAlias("iosAc", " iOS"); config.addHeaderAlias("androidAc", " 安卓");
writer.setOnlyAlias(true); config.addHeaderAlias("iosAc", " iOS");
config.setOnlyAlias(true);
// 设置合并的单元格 // 设置合并的单元格
writer.merge(new CellRangeAddress(0, 1, 0, 0), "日期", true); writer.merge(new CellRangeAddress(0, 1, 0, 0), "日期", true);

View File

@ -57,8 +57,9 @@ public class IssueI66Z6BTest {
final ExcelWriter writer = ExcelUtil.getWriter(destFile); final ExcelWriter writer = ExcelUtil.getWriter(destFile);
//自定义标题别名 //自定义标题别名
writer.addHeaderAlias("姓名", "name"); final ExcelWriteConfig config = writer.getConfig();
writer.addHeaderAlias("年龄", "age"); config.addHeaderAlias("姓名", "name");
config.addHeaderAlias("年龄", "age");
writer.write(dataList, true); writer.write(dataList, true);
writer.close(); writer.close();