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;
@ -34,19 +36,26 @@ import java.util.Map;
*/ */
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);
@ -55,7 +64,7 @@ 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) {
@ -65,7 +74,7 @@ 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) {
@ -75,8 +84,8 @@ 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) {
@ -86,8 +95,8 @@ 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) {
@ -97,7 +106,7 @@ 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) {
@ -107,7 +116,7 @@ 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) {
@ -125,6 +134,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
// ------------------------------------------------------------------------------------------------------- Constructor end // ------------------------------------------------------------------------------------------------------- Constructor end
// ------------------------------------------------------------------------------------------------------- Getters and Setters start // ------------------------------------------------------------------------------------------------------- Getters and Setters start
/** /**
* 是否忽略空行 * 是否忽略空行
* *
@ -181,7 +191,7 @@ 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) {
@ -225,10 +235,10 @@ 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,6 +265,45 @@ 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
@ -270,8 +319,8 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* 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) {
@ -307,7 +356,7 @@ 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的列表
*/ */
@ -318,10 +367,10 @@ 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
*/ */
@ -332,11 +381,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 endRowIndex 读取结束行包含从0开始计数 * @param endRowIndex 读取结束行包含从0开始计数
* @param beanType 每行对应Bean的类型 * @param beanType 每行对应Bean的类型
* @return Map的列表 * @return Map的列表
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -420,6 +469,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
} }
// ------------------------------------------------------------------------------------------------------- Private methods start // ------------------------------------------------------------------------------------------------------- Private methods start
/** /**
* 读取一行 * 读取一行
* *
@ -443,7 +493,7 @@ 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;
@ -453,12 +503,12 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* 转换标题别名如果没有别名则使用原标题当标题为空时列号对应的字母便是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);
} }

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));
}
} }