mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add reader
This commit is contained in:
parent
4c12094a7f
commit
50134d8822
@ -23,6 +23,7 @@
|
||||
* 【http 】 完善StrUtil的注释(pr#186@Gitee)
|
||||
* 【aop 】 去除调试日志(issue#1116@Github)
|
||||
* 【core 】 增加'反转义(pr#1121@Github)
|
||||
* 【poi 】 增加SheetReader和XXXRowHandler(issue#I1WHJP@Gitee)
|
||||
|
||||
### Bug修复
|
||||
* 【crypto 】 修复SM2验签后无法解密问题(issue#I1W0VP@Gitee)
|
||||
|
@ -1,12 +1,13 @@
|
||||
package cn.hutool.cron.demo;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.cron.CronUtil;
|
||||
import cn.hutool.cron.TaskExecutor;
|
||||
import cn.hutool.cron.listener.TaskListener;
|
||||
import cn.hutool.cron.task.Task;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 定时任务样例
|
||||
@ -38,6 +39,23 @@ public class CronTest {
|
||||
@Test
|
||||
@Ignore
|
||||
public void cronTest2() {
|
||||
CronUtil.getScheduler().addListener(new TaskListener() {
|
||||
@Override
|
||||
public void onStart(TaskExecutor executor) {
|
||||
Console.log("Listen task start!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSucceeded(TaskExecutor executor) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(TaskExecutor executor, Throwable exception) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 支持秒级别定时任务
|
||||
CronUtil.setMatchSecond(true);
|
||||
CronUtil.start();
|
||||
|
@ -0,0 +1,45 @@
|
||||
package cn.hutool.poi.excel;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.extractor.ExcelExtractor;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.extractor.XSSFExcelExtractor;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
/**
|
||||
* {@link ExcelExtractor}工具封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public class ExcelExtractorUtil {
|
||||
/**
|
||||
* 获取 {@link ExcelExtractor} 对象
|
||||
*
|
||||
* @return {@link ExcelExtractor}
|
||||
*/
|
||||
public static ExcelExtractor getExtractor(Workbook wb) {
|
||||
ExcelExtractor extractor;
|
||||
if (wb instanceof HSSFWorkbook) {
|
||||
extractor = new org.apache.poi.hssf.extractor.ExcelExtractor((HSSFWorkbook) wb);
|
||||
} else {
|
||||
extractor = new XSSFExcelExtractor((XSSFWorkbook) wb);
|
||||
}
|
||||
return extractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取为文本格式<br>
|
||||
* 使用{@link ExcelExtractor} 提取Excel内容
|
||||
*
|
||||
* @param wb {@link Workbook}
|
||||
* @param withSheetName 是否附带sheet名
|
||||
* @return Excel文本
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public static String readAsText(Workbook wb, boolean withSheetName) {
|
||||
final ExcelExtractor extractor = getExtractor(wb);
|
||||
extractor.setIncludeSheetNames(withSheetName);
|
||||
return extractor.getText();
|
||||
}
|
||||
}
|
@ -1,28 +1,22 @@
|
||||
package cn.hutool.poi.excel;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.poi.excel.cell.CellEditor;
|
||||
import cn.hutool.poi.excel.cell.CellHandler;
|
||||
import cn.hutool.poi.excel.cell.CellUtil;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import cn.hutool.poi.excel.reader.BeanSheetReader;
|
||||
import cn.hutool.poi.excel.reader.ListSheetReader;
|
||||
import cn.hutool.poi.excel.reader.MapSheetReader;
|
||||
import cn.hutool.poi.excel.reader.SheetReader;
|
||||
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.apache.poi.xssf.extractor.XSSFExcelExtractor;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -256,37 +250,31 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取工作簿中指定的Sheet
|
||||
* 读取工作簿中指定的Sheet,此方法会把第一行作为标题行,替换标题别名
|
||||
*
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
* @return 行的集合,一行使用List表示
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public List<List<Object>> read(int startRowIndex, int endRowIndex) {
|
||||
checkNotClosed();
|
||||
List<List<Object>> resultList = new ArrayList<>();
|
||||
return read(startRowIndex, endRowIndex, true);
|
||||
}
|
||||
|
||||
startRowIndex = Math.max(startRowIndex, this.sheet.getFirstRowNum());// 读取起始行(包含)
|
||||
endRowIndex = Math.min(endRowIndex, this.sheet.getLastRowNum());// 读取结束行(包含)
|
||||
boolean isFirstLine = true;
|
||||
List rowList;
|
||||
for (int i = startRowIndex; i <= endRowIndex; i++) {
|
||||
rowList = readRow(i);
|
||||
if (CollUtil.isNotEmpty(rowList) || false == ignoreEmptyRow) {
|
||||
if (null == rowList) {
|
||||
rowList = new ArrayList<>(0);
|
||||
}
|
||||
if (isFirstLine) {
|
||||
isFirstLine = false;
|
||||
if (MapUtil.isNotEmpty(this.headerAlias)) {
|
||||
rowList = aliasHeader(rowList);
|
||||
}
|
||||
}
|
||||
resultList.add(rowList);
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
/**
|
||||
* 读取工作簿中指定的Sheet
|
||||
*
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
* @param aliasFirstLine 是否首行作为标题行转换别名
|
||||
* @return 行的集合,一行使用List表示
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public List<List<Object>> read(int startRowIndex, int endRowIndex, boolean aliasFirstLine) {
|
||||
final ListSheetReader reader = new ListSheetReader(startRowIndex, endRowIndex, aliasFirstLine);
|
||||
reader.setCellEditor(this.cellEditor);
|
||||
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
|
||||
reader.setHeaderAlias(headerAlias);
|
||||
return read(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,33 +336,11 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
* @return Map的列表
|
||||
*/
|
||||
public List<Map<String, Object>> read(int headerRowIndex, int startRowIndex, int endRowIndex) {
|
||||
checkNotClosed();
|
||||
// 边界判断
|
||||
final int firstRowNum = sheet.getFirstRowNum();
|
||||
final int lastRowNum = sheet.getLastRowNum();
|
||||
if (headerRowIndex < firstRowNum) {
|
||||
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, firstRowNum));
|
||||
}
|
||||
startRowIndex = Math.max(startRowIndex, firstRowNum);// 读取起始行(包含)
|
||||
endRowIndex = Math.min(endRowIndex, lastRowNum);// 读取结束行(包含)
|
||||
|
||||
// 读取header
|
||||
List<Object> headerList = readRow(sheet.getRow(headerRowIndex));
|
||||
|
||||
final List<Map<String, Object>> result = new ArrayList<>(endRowIndex - startRowIndex + 1);
|
||||
List<Object> rowList;
|
||||
for (int i = startRowIndex; i <= endRowIndex; i++) {
|
||||
if (i != headerRowIndex) {
|
||||
// 跳过标题行
|
||||
rowList = readRow(sheet.getRow(i));
|
||||
if (CollUtil.isNotEmpty(rowList) || false == ignoreEmptyRow) {
|
||||
result.add(IterUtil.toMap(aliasHeader(headerList), rowList, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
final MapSheetReader reader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex);
|
||||
reader.setCellEditor(this.cellEditor);
|
||||
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
|
||||
reader.setHeaderAlias(headerAlias);
|
||||
return read(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -412,19 +378,25 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
* @param beanType 每行对应Bean的类型
|
||||
* @return Map的列表
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> read(int headerRowIndex, int startRowIndex, int endRowIndex, Class<T> beanType) {
|
||||
checkNotClosed();
|
||||
final List<Map<String, Object>> mapList = read(headerRowIndex, startRowIndex, endRowIndex);
|
||||
if (Map.class.isAssignableFrom(beanType)) {
|
||||
return (List<T>) mapList;
|
||||
}
|
||||
final BeanSheetReader<T> reader = new BeanSheetReader<>(headerRowIndex, startRowIndex, endRowIndex, beanType);
|
||||
reader.setCellEditor(this.cellEditor);
|
||||
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
|
||||
reader.setHeaderAlias(headerAlias);
|
||||
return read(reader);
|
||||
}
|
||||
|
||||
final List<T> beanList = new ArrayList<>(mapList.size());
|
||||
for (Map<String, Object> map : mapList) {
|
||||
beanList.add(BeanUtil.toBean(map, beanType));
|
||||
}
|
||||
return beanList;
|
||||
/**
|
||||
* 读取数据为指定类型
|
||||
*
|
||||
* @param <T> 读取数据类型
|
||||
* @param sheetReader {@link SheetReader}实现
|
||||
* @return 数据读取结果
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public <T> T read(SheetReader<T> sheetReader){
|
||||
checkNotClosed();
|
||||
return Assert.notNull(sheetReader).read(this.sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -436,9 +408,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public String readAsText(boolean withSheetName) {
|
||||
final ExcelExtractor extractor = getExtractor();
|
||||
extractor.setIncludeSheetNames(withSheetName);
|
||||
return extractor.getText();
|
||||
return ExcelExtractorUtil.readAsText(this.workbook, withSheetName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,14 +418,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public ExcelExtractor getExtractor() {
|
||||
ExcelExtractor extractor;
|
||||
Workbook wb = this.workbook;
|
||||
if (wb instanceof HSSFWorkbook) {
|
||||
extractor = new org.apache.poi.hssf.extractor.ExcelExtractor((HSSFWorkbook) wb);
|
||||
} else {
|
||||
extractor = new XSSFExcelExtractor((XSSFWorkbook) wb);
|
||||
}
|
||||
return extractor;
|
||||
return ExcelExtractorUtil.getExtractor(this.workbook);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -504,42 +467,6 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
|
||||
return RowUtil.readRow(row, this.cellEditor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header
|
||||
*
|
||||
* @param headerList 原标题列表
|
||||
* @return 转换别名列表
|
||||
*/
|
||||
private List<String> aliasHeader(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
|
||||
*/
|
||||
private String aliasHeader(Object headerObj, int index) {
|
||||
if (null == headerObj) {
|
||||
return ExcelUtil.indexToColName(index);
|
||||
}
|
||||
|
||||
final String header = headerObj.toString();
|
||||
return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否未关闭状态
|
||||
*/
|
||||
|
@ -0,0 +1,140 @@
|
||||
package cn.hutool.poi.excel.reader;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.poi.excel.ExcelUtil;
|
||||
import cn.hutool.poi.excel.RowUtil;
|
||||
import cn.hutool.poi.excel.cell.CellEditor;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 抽象{@link Sheet}数据读取实现
|
||||
*
|
||||
* @param <T> 读取类型
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public abstract class AbstractSheetReader<T> implements SheetReader<T> {
|
||||
|
||||
/**
|
||||
* 读取起始行(包含,从0开始计数)
|
||||
*/
|
||||
protected final int startRowIndex;
|
||||
/**
|
||||
* 读取结束行(包含,从0开始计数)
|
||||
*/
|
||||
protected final int endRowIndex;
|
||||
/**
|
||||
* 是否忽略空行
|
||||
*/
|
||||
protected boolean ignoreEmptyRow = true;
|
||||
/**
|
||||
* 单元格值处理接口
|
||||
*/
|
||||
protected CellEditor cellEditor;
|
||||
/**
|
||||
* 标题别名
|
||||
*/
|
||||
private Map<String, String> headerAlias = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
*/
|
||||
public AbstractSheetReader(int startRowIndex, int endRowIndex) {
|
||||
this.startRowIndex = startRowIndex;
|
||||
this.endRowIndex = endRowIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单元格值处理逻辑<br>
|
||||
* 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等
|
||||
*
|
||||
* @param cellEditor 单元格值处理接口
|
||||
*/
|
||||
public void setCellEditor(CellEditor cellEditor) {
|
||||
this.cellEditor = cellEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否忽略空行
|
||||
*
|
||||
* @param ignoreEmptyRow 是否忽略空行
|
||||
*/
|
||||
public void setIgnoreEmptyRow(boolean ignoreEmptyRow) {
|
||||
this.ignoreEmptyRow = ignoreEmptyRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题行的别名Map
|
||||
*
|
||||
* @param headerAlias 别名Map
|
||||
*/
|
||||
public void setHeaderAlias(Map<String, String> headerAlias) {
|
||||
this.headerAlias = headerAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加标题别名
|
||||
*
|
||||
* @param header 标题
|
||||
* @param alias 别名
|
||||
*/
|
||||
public void addHeaderAlias(String header, String alias) {
|
||||
this.headerAlias.put(header, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换标题别名,如果没有别名则使用原标题,当标题为空时,列号对应的字母便是header
|
||||
*
|
||||
* @param headerList 原标题列表
|
||||
* @return 转换别名列表
|
||||
*/
|
||||
protected List<String> aliasHeader(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(Object headerObj, int index) {
|
||||
if (null == headerObj) {
|
||||
return ExcelUtil.indexToColName(index);
|
||||
}
|
||||
|
||||
final String header = headerObj.toString();
|
||||
return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取某一行数据
|
||||
*
|
||||
* @param rowIndex 行号,从0开始
|
||||
* @return 一行数据
|
||||
* @since 4.0.3
|
||||
*/
|
||||
protected List<Object> readRow(Sheet sheet, int rowIndex) {
|
||||
return RowUtil.readRow(sheet.getRow(rowIndex), this.cellEditor);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package cn.hutool.poi.excel.reader;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.poi.excel.cell.CellEditor;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 读取{@link Sheet}为bean的List列表形式
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public class BeanSheetReader<T> implements SheetReader<List<T>> {
|
||||
|
||||
private final Class<T> beanClass;
|
||||
private final MapSheetReader mapSheetReader;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
* @param beanClass 每行对应Bean的类型
|
||||
*/
|
||||
public BeanSheetReader(int headerRowIndex, int startRowIndex, int endRowIndex, Class<T> beanClass) {
|
||||
mapSheetReader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex);
|
||||
this.beanClass = beanClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<T> read(Sheet sheet) {
|
||||
final List<Map<String, Object>> mapList = mapSheetReader.read(sheet);
|
||||
if (Map.class.isAssignableFrom(this.beanClass)) {
|
||||
return (List<T>) mapList;
|
||||
}
|
||||
|
||||
final List<T> beanList = new ArrayList<>(mapList.size());
|
||||
for (Map<String, Object> map : mapList) {
|
||||
beanList.add(BeanUtil.toBean(map, this.beanClass));
|
||||
}
|
||||
return beanList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置单元格值处理逻辑<br>
|
||||
* 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等
|
||||
*
|
||||
* @param cellEditor 单元格值处理接口
|
||||
*/
|
||||
public void setCellEditor(CellEditor cellEditor) {
|
||||
this.mapSheetReader.setCellEditor(cellEditor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否忽略空行
|
||||
*
|
||||
* @param ignoreEmptyRow 是否忽略空行
|
||||
*/
|
||||
public void setIgnoreEmptyRow(boolean ignoreEmptyRow) {
|
||||
this.mapSheetReader.setIgnoreEmptyRow(ignoreEmptyRow);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题行的别名Map
|
||||
*
|
||||
* @param headerAlias 别名Map
|
||||
*/
|
||||
public void setHeaderAlias(Map<String, String> headerAlias) {
|
||||
this.mapSheetReader.setHeaderAlias(headerAlias);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加标题别名
|
||||
*
|
||||
* @param header 标题
|
||||
* @param alias 别名
|
||||
*/
|
||||
public void addHeaderAlias(String header, String alias) {
|
||||
this.mapSheetReader.addHeaderAlias(header, alias);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package cn.hutool.poi.excel.reader;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 读取{@link Sheet}为List列表形式
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public class ListSheetReader extends AbstractSheetReader<List<List<Object>>> {
|
||||
|
||||
private final boolean aliasFirstLine;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
*/
|
||||
public ListSheetReader(int startRowIndex, int endRowIndex, boolean aliasFirstLine) {
|
||||
super(startRowIndex, endRowIndex);
|
||||
this.aliasFirstLine = aliasFirstLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<List<Object>> read(Sheet sheet) {
|
||||
final List<List<Object>> resultList = new ArrayList<>();
|
||||
|
||||
int startRowIndex = Math.max(this.startRowIndex, sheet.getFirstRowNum());// 读取起始行(包含)
|
||||
int endRowIndex = Math.min(this.endRowIndex, sheet.getLastRowNum());// 读取结束行(包含)
|
||||
List<Object> rowList;
|
||||
for (int i = startRowIndex; i <= endRowIndex; i++) {
|
||||
rowList = readRow(sheet, i);
|
||||
if (CollUtil.isNotEmpty(rowList) || false == ignoreEmptyRow) {
|
||||
if (aliasFirstLine && i == startRowIndex) {
|
||||
// 第一行作为标题行,替换别名
|
||||
rowList = Convert.toList(Object.class, aliasHeader(rowList));
|
||||
}
|
||||
resultList.add(rowList);
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package cn.hutool.poi.excel.reader;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 读取{@link Sheet}为Map的List列表形式
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public class MapSheetReader extends AbstractSheetReader<List<Map<String, Object>>> {
|
||||
|
||||
private final int headerRowIndex;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略
|
||||
* @param startRowIndex 起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 结束行(包含,从0开始计数)
|
||||
*/
|
||||
public MapSheetReader(int headerRowIndex, int startRowIndex, int endRowIndex) {
|
||||
super(startRowIndex, endRowIndex);
|
||||
this.headerRowIndex = headerRowIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> read(Sheet sheet) {
|
||||
// 边界判断
|
||||
final int firstRowNum = sheet.getFirstRowNum();
|
||||
final int lastRowNum = sheet.getLastRowNum();
|
||||
if (headerRowIndex < firstRowNum) {
|
||||
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, firstRowNum));
|
||||
}
|
||||
final int startRowIndex = Math.max(this.startRowIndex, firstRowNum);// 读取起始行(包含)
|
||||
final int endRowIndex = Math.min(this.endRowIndex, lastRowNum);// 读取结束行(包含)
|
||||
|
||||
// 读取header
|
||||
List<String> headerList = aliasHeader(readRow(sheet, headerRowIndex));
|
||||
|
||||
final List<Map<String, Object>> result = new ArrayList<>(endRowIndex - startRowIndex + 1);
|
||||
List<Object> rowList;
|
||||
for (int i = startRowIndex; i <= endRowIndex; i++) {
|
||||
// 跳过标题行
|
||||
if (i != headerRowIndex) {
|
||||
rowList = readRow(sheet, i);
|
||||
if (CollUtil.isNotEmpty(rowList) || false == ignoreEmptyRow) {
|
||||
result.add(IterUtil.toMap(headerList, rowList, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.hutool.poi.excel.reader;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* Excel {@link Sheet}读取接口,通过实现此接口,将{@link Sheet}中的数据读取为不同类型。
|
||||
*
|
||||
* @param <T> 读取的数据类型
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface SheetReader<T> {
|
||||
|
||||
/**
|
||||
* 读取数据
|
||||
*
|
||||
* @param sheet {@link Sheet}
|
||||
* @return 读取结果
|
||||
*/
|
||||
T read(Sheet sheet);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 数据读取接口及实现,此包中定义了SheetReader,通过实现此接口,实现sheet中的数据读取为不同类型。
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.poi.excel.reader;
|
@ -0,0 +1,59 @@
|
||||
package cn.hutool.poi.excel.sax.handler;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.func.Func1;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 抽象行数据处理器,通过实现{@link #handle(int, long, List)} 处理原始数据<br>
|
||||
* 并调用{@link #handleData(int, long, Object)}处理经过转换后的数据。
|
||||
*
|
||||
* @param <T> 转换后的数据类型
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public abstract class AbstractRowHandler<T> implements RowHandler {
|
||||
|
||||
/**
|
||||
* 读取起始行(包含,从0开始计数)
|
||||
*/
|
||||
protected final int startRowIndex;
|
||||
/**
|
||||
* 读取结束行(包含,从0开始计数)
|
||||
*/
|
||||
protected final int endRowIndex;
|
||||
/**
|
||||
* 行数据转换函数
|
||||
*/
|
||||
protected Func1<List<Object>, T> convertFunc;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param startRowIndex 读取起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 读取结束行(包含,从0开始计数)
|
||||
*/
|
||||
public AbstractRowHandler(int startRowIndex, int endRowIndex) {
|
||||
this.startRowIndex = startRowIndex;
|
||||
this.endRowIndex = endRowIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(int sheetIndex, long rowIndex, List<Object> rowList) {
|
||||
Assert.notNull(convertFunc);
|
||||
if (rowIndex < this.startRowIndex || rowIndex > this.endRowIndex) {
|
||||
return;
|
||||
}
|
||||
handleData(sheetIndex, rowIndex, convertFunc.callWithRuntimeException(rowList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理转换后的数据
|
||||
*
|
||||
* @param sheetIndex 当前Sheet序号
|
||||
* @param rowIndex 当前行号,从0开始计数
|
||||
* @param data 行数据
|
||||
*/
|
||||
public abstract void handleData(int sheetIndex, long rowIndex, T data);
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cn.hutool.poi.excel.sax.handler;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bean形式的行处理器<br>
|
||||
* 将一行数据转换为Map,key为指定行,value为当前行对应位置的值
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public abstract class BeanRowHandler<T> extends AbstractRowHandler<T>{
|
||||
|
||||
/**
|
||||
* 标题所在行(从0开始计数)
|
||||
*/
|
||||
private final int headerRowIndex;
|
||||
/**
|
||||
* 标题行
|
||||
*/
|
||||
List<String> headerList;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param headerRowIndex 标题所在行(从0开始计数)
|
||||
* @param startRowIndex 读取起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 读取结束行(包含,从0开始计数)
|
||||
*/
|
||||
public BeanRowHandler(int headerRowIndex, int startRowIndex, int endRowIndex, Class<T> clazz){
|
||||
super(startRowIndex, endRowIndex);
|
||||
Assert.isTrue(headerRowIndex <= startRowIndex, "Header row must before the start row!");
|
||||
this.headerRowIndex = headerRowIndex;
|
||||
this.convertFunc = (rowList)-> BeanUtil.toBean(IterUtil.toMap(headerList, rowList), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(int sheetIndex, long rowIndex, List<Object> rowList) {
|
||||
if (rowIndex == this.headerRowIndex) {
|
||||
this.headerList = ListUtil.unmodifiable(Convert.toList(String.class, rowList));
|
||||
return;
|
||||
}
|
||||
super.handle(sheetIndex, rowIndex, rowList);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package cn.hutool.poi.excel.sax.handler;
|
||||
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map形式的行处理器<br>
|
||||
* 将一行数据转换为Map,key为指定行,value为当前行对应位置的值
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.4.4
|
||||
*/
|
||||
public abstract class MapRowHandler extends AbstractRowHandler<Map<String, Object>> {
|
||||
|
||||
/**
|
||||
* 标题所在行(从0开始计数)
|
||||
*/
|
||||
private final int headerRowIndex;
|
||||
/**
|
||||
* 标题行
|
||||
*/
|
||||
List<String> headerList;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param headerRowIndex 标题所在行(从0开始计数)
|
||||
* @param startRowIndex 读取起始行(包含,从0开始计数)
|
||||
* @param endRowIndex 读取结束行(包含,从0开始计数)
|
||||
*/
|
||||
public MapRowHandler(int headerRowIndex, int startRowIndex, int endRowIndex){
|
||||
super(startRowIndex, endRowIndex);
|
||||
this.headerRowIndex = headerRowIndex;
|
||||
this.convertFunc = (rowList)-> IterUtil.toMap(headerList, rowList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(int sheetIndex, long rowIndex, List<Object> rowList) {
|
||||
if (rowIndex == this.headerRowIndex) {
|
||||
this.headerList = ListUtil.unmodifiable(Convert.toList(String.class, rowList));
|
||||
return;
|
||||
}
|
||||
super.handle(sheetIndex, rowIndex, rowList);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user