This commit is contained in:
Looly 2020-06-17 00:53:34 +08:00
parent b6a9bc6991
commit 3bca95c34a
5 changed files with 144 additions and 64 deletions

View File

@ -15,6 +15,7 @@
* 【core 】 增加StrUtil.removeAny方法(issue#923@Github) * 【core 】 增加StrUtil.removeAny方法(issue#923@Github)
* 【db 】 增加部分Connection参数支持(issue#924@Github) * 【db 】 增加部分Connection参数支持(issue#924@Github)
* 【core 】 FileUtil增加别名方法(pr#926@Github) * 【core 】 FileUtil增加别名方法(pr#926@Github)
* 【poi 】 EcelReader中增加read重载提供每个单元格单独处理的方法(issue#I1JZTL@Gitee)
### Bug修复 ### Bug修复
* 【json 】 修复append方法导致的JSONConfig传递失效问题issue#906@Github * 【json 】 修复append方法导致的JSONConfig传递失效问题issue#906@Github

View File

@ -9,9 +9,11 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.cell.CellEditor; import cn.hutool.poi.excel.cell.CellEditor;
import cn.hutool.poi.excel.cell.CellHandler;
import cn.hutool.poi.excel.cell.CellUtil; import cn.hutool.poi.excel.cell.CellUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
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.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;
@ -28,25 +30,32 @@ import java.util.Map;
/** /**
* Excel读取器<br> * Excel读取器<br>
* 读取Excel工作簿 * 读取Excel工作簿
* *
* @author Looly * @author Looly
* @since 3.1.0 * @since 3.1.0
*/ */
public class ExcelReader extends ExcelBase<ExcelReader> { public class ExcelReader extends ExcelBase<ExcelReader> {
/** 是否忽略空行 */ /**
* 是否忽略空行
*/
private boolean ignoreEmptyRow = true; private boolean ignoreEmptyRow = true;
/** 单元格值处理接口 */ /**
* 单元格值处理接口
*/
private CellEditor cellEditor; private CellEditor cellEditor;
/** 标题别名 */ /**
* 标题别名
*/
private Map<String, String> headerAlias = new HashMap<>(); private Map<String, String> headerAlias = new HashMap<>();
// ------------------------------------------------------------------------------------------------------- Constructor start // ------------------------------------------------------------------------------------------------------- Constructor start
/** /**
* 构造 * 构造
* *
* @param excelFilePath Excel文件路径绝对路径或相对于ClassPath路径 * @param excelFilePath Excel文件路径绝对路径或相对于ClassPath路径
* @param sheetIndex sheet序号0表示第一个sheet * @param sheetIndex sheet序号0表示第一个sheet
*/ */
public ExcelReader(String excelFilePath, int sheetIndex) { public ExcelReader(String excelFilePath, int sheetIndex) {
this(FileUtil.file(excelFilePath), sheetIndex); this(FileUtil.file(excelFilePath), sheetIndex);
@ -54,8 +63,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param bookFile Excel文件 * @param bookFile Excel文件
* @param sheetIndex sheet序号0表示第一个sheet * @param sheetIndex sheet序号0表示第一个sheet
*/ */
public ExcelReader(File bookFile, int sheetIndex) { public ExcelReader(File bookFile, int sheetIndex) {
@ -64,8 +73,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param bookFile Excel文件 * @param bookFile Excel文件
* @param sheetName sheet名第一个默认是sheet1 * @param sheetName sheet名第一个默认是sheet1
*/ */
public ExcelReader(File bookFile, String sheetName) { public ExcelReader(File bookFile, String sheetName) {
@ -74,9 +83,9 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param bookStream Excel文件的流 * @param bookStream Excel文件的流
* @param sheetIndex sheet序号0表示第一个sheet * @param sheetIndex sheet序号0表示第一个sheet
* @param closeAfterRead 读取结束是否关闭流 * @param closeAfterRead 读取结束是否关闭流
*/ */
public ExcelReader(InputStream bookStream, int sheetIndex, boolean closeAfterRead) { public ExcelReader(InputStream bookStream, int sheetIndex, boolean closeAfterRead) {
@ -85,9 +94,9 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param bookStream Excel文件的流 * @param bookStream Excel文件的流
* @param sheetName sheet名第一个默认是sheet1 * @param sheetName sheet名第一个默认是sheet1
* @param closeAfterRead 读取结束是否关闭流 * @param closeAfterRead 读取结束是否关闭流
*/ */
public ExcelReader(InputStream bookStream, String sheetName, boolean closeAfterRead) { public ExcelReader(InputStream bookStream, String sheetName, boolean closeAfterRead) {
@ -96,8 +105,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param book {@link Workbook} 表示一个Excel文件 * @param book {@link Workbook} 表示一个Excel文件
* @param sheetIndex sheet序号0表示第一个sheet * @param sheetIndex sheet序号0表示第一个sheet
*/ */
public ExcelReader(Workbook book, int sheetIndex) { public ExcelReader(Workbook book, int sheetIndex) {
@ -106,8 +115,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param book {@link Workbook} 表示一个Excel文件 * @param book {@link Workbook} 表示一个Excel文件
* @param sheetName sheet名第一个默认是sheet1 * @param sheetName sheet名第一个默认是sheet1
*/ */
public ExcelReader(Workbook book, String sheetName) { public ExcelReader(Workbook book, String sheetName) {
@ -116,7 +125,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 构造 * 构造
* *
* @param sheet Excel中的sheet * @param sheet Excel中的sheet
*/ */
public ExcelReader(Sheet sheet) { public ExcelReader(Sheet sheet) {
@ -125,9 +134,10 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
// ------------------------------------------------------------------------------------------------------- Constructor end // ------------------------------------------------------------------------------------------------------- Constructor end
// ------------------------------------------------------------------------------------------------------- Getters and Setters start // ------------------------------------------------------------------------------------------------------- Getters and Setters start
/** /**
* 是否忽略空行 * 是否忽略空行
* *
* @return 是否忽略空行 * @return 是否忽略空行
*/ */
public boolean isIgnoreEmptyRow() { public boolean isIgnoreEmptyRow() {
@ -136,7 +146,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 设置是否忽略空行 * 设置是否忽略空行
* *
* @param ignoreEmptyRow 是否忽略空行 * @param ignoreEmptyRow 是否忽略空行
* @return this * @return this
*/ */
@ -148,7 +158,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 设置单元格值处理逻辑<br> * 设置单元格值处理逻辑<br>
* 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等 * 当Excel中的值并不能满足我们的读取要求时通过传入一个编辑接口可以对单元格值自定义例如对数字和日期类型值转换为字符串等
* *
* @param cellEditor 单元格值处理接口 * @param cellEditor 单元格值处理接口
* @return this * @return this
*/ */
@ -159,7 +169,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 获得标题行的别名Map * 获得标题行的别名Map
* *
* @return 别名Map * @return 别名Map
*/ */
public Map<String, String> getHeaderAlias() { public Map<String, String> getHeaderAlias() {
@ -168,7 +178,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 设置标题行的别名Map * 设置标题行的别名Map
* *
* @param headerAlias 别名Map * @param headerAlias 别名Map
* @return this * @return this
*/ */
@ -179,9 +189,9 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 增加标题别名 * 增加标题别名
* *
* @param header 标题 * @param header 标题
* @param alias 别名 * @param alias 别名
* @return this * @return this
*/ */
public ExcelReader addHeaderAlias(String header, String alias) { public ExcelReader addHeaderAlias(String header, String alias) {
@ -191,7 +201,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 去除标题别名 * 去除标题别名
* *
* @param header 标题 * @param header 标题
* @return this * @return this
*/ */
@ -203,7 +213,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取工作簿中指定的Sheet的所有行列数据 * 读取工作簿中指定的Sheet的所有行列数据
* *
* @return 行的集合一行使用List表示 * @return 行的集合一行使用List表示
*/ */
public List<List<Object>> read() { public List<List<Object>> read() {
@ -212,7 +222,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取工作簿中指定的Sheet * 读取工作簿中指定的Sheet
* *
* @param startRowIndex 起始行包含从0开始计数 * @param startRowIndex 起始行包含从0开始计数
* @return 行的集合一行使用List表示 * @return 行的集合一行使用List表示
* @since 4.0.0 * @since 4.0.0
@ -223,12 +233,12 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取工作簿中指定的Sheet * 读取工作簿中指定的Sheet
* *
* @param startRowIndex 起始行包含从0开始计数 * @param startRowIndex 起始行包含从0开始计数
* @param endRowIndex 结束行包含从0开始计数 * @param endRowIndex 结束行包含从0开始计数
* @return 行的集合一行使用List表示 * @return 行的集合一行使用List表示
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({"rawtypes", "unchecked"})
public List<List<Object>> read(int startRowIndex, int endRowIndex) { public List<List<Object>> read(int startRowIndex, int endRowIndex) {
checkNotClosed(); checkNotClosed();
List<List<Object>> resultList = new ArrayList<>(); List<List<Object>> resultList = new ArrayList<>();
@ -255,10 +265,49 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
return resultList; return resultList;
} }
/**
* 读取工作簿中指定的Sheet此方法为类流处理方式当读到指定单元格时会调用CellEditor接口<br>
* 用户通过实现此接口可以更加灵活的处理每个单元格的数据
*
* @param cellHandler 单元格处理器用于处理读到的单元格及其数据
* @since 5.3.8
*/
public void read(CellHandler cellHandler) {
read(0, Integer.MAX_VALUE, cellHandler);
}
/**
* 读取工作簿中指定的Sheet此方法为类流处理方式当读到指定单元格时会调用CellEditor接口<br>
* 用户通过实现此接口可以更加灵活的处理每个单元格的数据
*
* @param startRowIndex 起始行包含从0开始计数
* @param endRowIndex 结束行包含从0开始计数
* @param cellHandler 单元格处理器用于处理读到的单元格及其数据
* @since 5.3.8
*/
public void read(int startRowIndex, int endRowIndex, CellHandler 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);
columnSize = row.getLastCellNum();
Cell cell;
for (short x = 0; x < columnSize; x++) {
cell = row.getCell(x);
cellHandler.handle(cell, CellUtil.getCellValue(cell));
}
}
}
/** /**
* 读取Excel为Map的列表读取所有行默认第一行做为标题数据从第二行开始<br> * 读取Excel为Map的列表读取所有行默认第一行做为标题数据从第二行开始<br>
* Map表示一行标题为key单元格内容为value * Map表示一行标题为key单元格内容为value
* *
* @return Map的列表 * @return Map的列表
*/ */
public List<Map<String, Object>> readAll() { public List<Map<String, Object>> readAll() {
@ -268,10 +317,10 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取Excel为Map的列表<br> * 读取Excel为Map的列表<br>
* Map表示一行标题为key单元格内容为value * Map表示一行标题为key单元格内容为value
* *
* @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略 * @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略
* @param startRowIndex 起始行包含从0开始计数 * @param startRowIndex 起始行包含从0开始计数
* @param endRowIndex 读取结束行包含从0开始计数 * @param endRowIndex 读取结束行包含从0开始计数
* @return Map的列表 * @return Map的列表
*/ */
public List<Map<String, Object>> read(int headerRowIndex, int startRowIndex, int endRowIndex) { public List<Map<String, Object>> read(int headerRowIndex, int startRowIndex, int endRowIndex) {
@ -306,8 +355,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取Excel为Bean的列表读取所有行默认第一行做为标题数据从第二行开始 * 读取Excel为Bean的列表读取所有行默认第一行做为标题数据从第二行开始
* *
* @param <T> Bean类型 * @param <T> Bean类型
* @param beanType 每行对应Bean的类型 * @param beanType 每行对应Bean的类型
* @return Map的列表 * @return Map的列表
*/ */
@ -317,11 +366,11 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取Excel为Bean的列表 * 读取Excel为Bean的列表
* *
* @param <T> Bean类型 * @param <T> Bean类型
* @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略从0开始计数 * @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略从0开始计数
* @param startRowIndex 起始行包含从0开始计数 * @param startRowIndex 起始行包含从0开始计数
* @param beanType 每行对应Bean的类型 * @param beanType 每行对应Bean的类型
* @return Map的列表 * @return Map的列表
* @since 4.0.1 * @since 4.0.1
*/ */
@ -331,12 +380,12 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取Excel为Bean的列表 * 读取Excel为Bean的列表
* *
* @param <T> Bean类型 * @param <T> Bean类型
* @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略从0开始计数 * @param headerRowIndex 标题所在行如果标题行在读取的内容行中间这行做为数据将忽略从0开始计数
* @param startRowIndex 起始行包含从0开始计数 * @param startRowIndex 起始行包含从0开始计数
* @param endRowIndex 读取结束行包含从0开始计数 * @param endRowIndex 读取结束行包含从0开始计数
* @param beanType 每行对应Bean的类型 * @param beanType 每行对应Bean的类型
* @return Map的列表 * @return Map的列表
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -357,7 +406,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取为文本格式<br> * 读取为文本格式<br>
* 使用{@link ExcelExtractor} 提取Excel内容 * 使用{@link ExcelExtractor} 提取Excel内容
* *
* @param withSheetName 是否附带sheet名 * @param withSheetName 是否附带sheet名
* @return Excel文本 * @return Excel文本
* @since 4.1.0 * @since 4.1.0
@ -370,7 +419,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 获取 {@link ExcelExtractor} 对象 * 获取 {@link ExcelExtractor} 对象
* *
* @return {@link ExcelExtractor} * @return {@link ExcelExtractor}
* @since 4.1.0 * @since 4.1.0
*/ */
@ -387,7 +436,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取某一行数据 * 读取某一行数据
* *
* @param rowIndex 行号从0开始 * @param rowIndex 行号从0开始
* @return 一行数据 * @return 一行数据
* @since 4.0.3 * @since 4.0.3
@ -398,7 +447,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 读取某个单元格的值 * 读取某个单元格的值
* *
* @param x X坐标从0计数即列号 * @param x X坐标从0计数即列号
* @param y Y坐标从0计数即行号 * @param y Y坐标从0计数即行号
* @return 如果单元格无值返回null * @return 如果单元格无值返回null
@ -411,7 +460,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 获取Excel写出器<br> * 获取Excel写出器<br>
* 在读取Excel并做一定编辑后获取写出器写出 * 在读取Excel并做一定编辑后获取写出器写出
* *
* @return {@link ExcelWriter} * @return {@link ExcelWriter}
* @since 4.0.6 * @since 4.0.6
*/ */
@ -420,9 +469,10 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
} }
// ------------------------------------------------------------------------------------------------------- Private methods start // ------------------------------------------------------------------------------------------------------- Private methods start
/** /**
* 读取一行 * 读取一行
* *
* @param row * @param row
* @return 单元格值列表 * @return 单元格值列表
*/ */
@ -432,7 +482,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/** /**
* 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header * 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header
* *
* @param headerList 原标题列表 * @param headerList 原标题列表
* @return 转换别名列表 * @return 转换别名列表
*/ */
@ -443,25 +493,25 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
return result; return result;
} }
for(int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
result.add(aliasHeader(headerList.get(i), i)); result.add(aliasHeader(headerList.get(i), i));
} }
return result; return result;
} }
/** /**
* 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header * 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是header
* *
* @param headerObj 原标题 * @param headerObj 原标题
* @param index 标题所在列号当标题为空时列号对应的字母便是header * @param index 标题所在列号当标题为空时列号对应的字母便是header
* @return 转换别名列表 * @return 转换别名列表
* @since 4.3.2 * @since 4.3.2
*/ */
private String aliasHeader(Object headerObj, int index) { private String aliasHeader(Object headerObj, int index) {
if(null == headerObj) { if (null == headerObj) {
return ExcelUtil.indexToColName(index); return ExcelUtil.indexToColName(index);
} }
final String header = headerObj.toString(); final String header = headerObj.toString();
return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header); return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header);
} }

View File

@ -4,13 +4,16 @@ import org.apache.poi.ss.usermodel.Cell;
/** /**
* 单元格编辑器接口 * 单元格编辑器接口
* @author Looly
* *
* @author Looly
*/ */
@FunctionalInterface
public interface CellEditor { public interface CellEditor {
/** /**
* 编辑 * 编辑
* @param cell 单元格对象可以获取单元格行列样式等信息 *
* @param cell 单元格对象可以获取单元格行列样式等信息
* @param value 单元格值 * @param value 单元格值
* @return 编辑后的对象 * @return 编辑后的对象
*/ */

View File

@ -0,0 +1,20 @@
package cn.hutool.poi.excel.cell;
import org.apache.poi.ss.usermodel.Cell;
/**
* 单元格处理器接口
*
* @author Looly
*/
@FunctionalInterface
public interface CellHandler {
/**
* 处理
*
* @param cell 单元格对象可以获取单元格行列样式等信息
* @param value 单元格值
*/
void handle(Cell cell, Object value);
}

View File

@ -208,4 +208,10 @@ public class ExcelReadTest {
Assert.assertEquals(11L, read.get(2).get(2)); Assert.assertEquals(11L, read.get(2).get(2));
} }
@Test
public void readCellsTest() {
final ExcelReader reader = ExcelUtil.getReader("merge_test.xlsx");
reader.read((cell, value)-> Console.log("{}, {} {}", cell.getRowIndex(), cell.getColumnIndex(), value));
}
} }