Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cn.hutool.poi.excel.ExcelReader Maven / Gradle / Ivy
package cn.hutool.poi.excel;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
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.reader.BeanSheetReader;
import cn.hutool.poi.excel.reader.ColumnSheetReader;
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 java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* Excel读取器
* 读取Excel工作簿
*
* @author Looly
* @since 3.1.0
*/
public class ExcelReader extends ExcelBase {
/**
* 是否忽略空行
*/
private boolean ignoreEmptyRow = true;
/**
* 单元格值处理接口
*/
private CellEditor cellEditor;
// ------------------------------------------------------------------------------------------------------- Constructor start
/**
* 构造
*
* @param excelFilePath Excel文件路径,绝对路径或相对于ClassPath路径
* @param sheetIndex sheet序号,0表示第一个sheet
*/
public ExcelReader(String excelFilePath, int sheetIndex) {
this(FileUtil.file(excelFilePath), sheetIndex);
}
/**
* 构造
*
* @param excelFilePath Excel文件路径,绝对路径或相对于ClassPath路径
* @param sheetName sheet名,第一个默认是sheet1
* @since 5.8.0
*/
public ExcelReader(String excelFilePath, String sheetName) {
this(FileUtil.file(excelFilePath), sheetName);
}
/**
* 构造(读写方式读取)
*
* @param bookFile Excel文件
* @param sheetIndex sheet序号,0表示第一个sheet
*/
public ExcelReader(File bookFile, int sheetIndex) {
this(WorkbookUtil.createBook(bookFile, true), sheetIndex);
this.destFile = bookFile;
}
/**
* 构造(读写方式读取)
*
* @param bookFile Excel文件
* @param sheetName sheet名,第一个默认是sheet1
*/
public ExcelReader(File bookFile, String sheetName) {
this(WorkbookUtil.createBook(bookFile, true), sheetName);
this.destFile = bookFile;
}
/**
* 构造(只读方式读取)
*
* @param bookStream Excel文件的流
* @param sheetIndex sheet序号,0表示第一个sheet
*/
public ExcelReader(InputStream bookStream, int sheetIndex) {
this(WorkbookUtil.createBook(bookStream), sheetIndex);
}
/**
* 构造(只读方式读取)
*
* @param bookStream Excel文件的流
* @param sheetName sheet名,第一个默认是sheet1
*/
public ExcelReader(InputStream bookStream, String sheetName) {
this(WorkbookUtil.createBook(bookStream), sheetName);
}
/**
* 构造
*
* @param book {@link Workbook} 表示一个Excel文件
* @param sheetIndex sheet序号,0表示第一个sheet
*/
public ExcelReader(Workbook book, int sheetIndex) {
this(getSheetOrCloseWorkbook(book, sheetIndex));
}
/**
* 构造
*
* @param book {@link Workbook} 表示一个Excel文件
* @param sheetName sheet名,第一个默认是sheet1
*/
public ExcelReader(Workbook book, String sheetName) {
this(getSheetOrCloseWorkbook(book, sheetName));
}
/**
* 构造
*
* @param sheet Excel中的sheet
*/
public ExcelReader(Sheet sheet) {
super(sheet);
}
// ------------------------------------------------------------------------------------------------------- Constructor end
// ------------------------------------------------------------------------------------------------------- Getters and Setters start
/**
* 是否忽略空行
*
* @return 是否忽略空行
*/
public boolean isIgnoreEmptyRow() {
return ignoreEmptyRow;
}
/**
* 设置是否忽略空行
*
* @param ignoreEmptyRow 是否忽略空行
* @return this
*/
public ExcelReader setIgnoreEmptyRow(boolean ignoreEmptyRow) {
this.ignoreEmptyRow = ignoreEmptyRow;
return this;
}
/**
* 设置单元格值处理逻辑
* 当Excel中的值并不能满足我们的读取要求时,通过传入一个编辑接口,可以对单元格值自定义,例如对数字和日期类型值转换为字符串等
*
* @param cellEditor 单元格值处理接口
* @return this
*/
public ExcelReader setCellEditor(CellEditor cellEditor) {
this.cellEditor = cellEditor;
return this;
}
// ------------------------------------------------------------------------------------------------------- Getters and Setters end
/**
* 读取工作簿中指定的Sheet的所有行列数据
*
* @return 行的集合,一行使用List表示
*/
public List> read() {
return read(0);
}
/**
* 读取工作簿中指定的Sheet
*
* @param startRowIndex 起始行(包含,从0开始计数)
* @return 行的集合,一行使用List表示
* @since 4.0.0
*/
public List> read(int startRowIndex) {
return read(startRowIndex, Integer.MAX_VALUE);
}
/**
* 读取工作簿中指定的Sheet,此方法会把第一行作为标题行,替换标题别名
*
* @param startRowIndex 起始行(包含,从0开始计数)
* @param endRowIndex 结束行(包含,从0开始计数)
* @return 行的集合,一行使用List表示
*/
public List> read(int startRowIndex, int endRowIndex) {
return read(startRowIndex, endRowIndex, true);
}
/**
* 读取工作簿中指定的Sheet
*
* @param startRowIndex 起始行(包含,从0开始计数)
* @param endRowIndex 结束行(包含,从0开始计数)
* @param aliasFirstLine 是否首行作为标题行转换别名
* @return 行的集合,一行使用List表示
* @since 5.4.4
*/
public List> 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);
}
/**
* 读取工作簿中指定的Sheet中指定列
*
* @param columnIndex 列号,从0开始计数
* @param startRowIndex 起始行(包含,从0开始计数)
* @return 列的集合
* @since 5.7.17
*/
public List readColumn(int columnIndex, int startRowIndex) {
return readColumn(columnIndex, startRowIndex, Integer.MAX_VALUE);
}
/**
* 读取工作簿中指定的Sheet中指定列
*
* @param columnIndex 列号,从0开始计数
* @param startRowIndex 起始行(包含,从0开始计数)
* @param endRowIndex 结束行(包含,从0开始计数)
* @return 列的集合
* @since 5.7.17
*/
public List readColumn(int columnIndex, int startRowIndex, int endRowIndex) {
final ColumnSheetReader reader = new ColumnSheetReader(columnIndex, startRowIndex, endRowIndex);
reader.setCellEditor(this.cellEditor);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader);
}
/**
* 读取工作簿中指定的Sheet,此方法为类流处理方式,当读到指定单元格时,会调用CellEditor接口
* 用户通过实现此接口,可以更加灵活地处理每个单元格的数据。
*
* @param cellHandler 单元格处理器,用于处理读到的单元格及其数据
* @since 5.3.8
*/
public void read(CellHandler cellHandler) {
read(0, Integer.MAX_VALUE, cellHandler);
}
/**
* 读取工作簿中指定的Sheet,此方法为类流处理方式,当读到指定单元格时,会调用CellEditor接口
* 用户通过实现此接口,可以更加灵活地处理每个单元格的数据。
*
* @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);
if (null != row) {
columnSize = row.getLastCellNum();
Cell cell;
for (short x = 0; x < columnSize; x++) {
cell = row.getCell(x);
cellHandler.handle(cell, CellUtil.getCellValue(cell));
}
}
}
}
/**
* 读取Excel为Map的列表,读取所有行,默认第一行做为标题,数据从第二行开始
* Map表示一行,标题为key,单元格内容为value
*
* @return Map的列表
*/
public List> readAll() {
return read(0, 1, Integer.MAX_VALUE);
}
/**
* 读取Excel为Map的列表
* Map表示一行,标题为key,单元格内容为value
*
* @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略
* @param startRowIndex 起始行(包含,从0开始计数)
* @param endRowIndex 读取结束行(包含,从0开始计数)
* @return Map的列表
*/
public List> read(int headerRowIndex, int startRowIndex, int endRowIndex) {
final MapSheetReader reader = new MapSheetReader(headerRowIndex, startRowIndex, endRowIndex);
reader.setCellEditor(this.cellEditor);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader);
}
/**
* 读取Excel为Bean的列表,读取所有行,默认第一行做为标题,数据从第二行开始
*
* @param Bean类型
* @param beanType 每行对应Bean的类型
* @return Map的列表
*/
public List readAll(Class beanType) {
return read(0, 1, Integer.MAX_VALUE, beanType);
}
/**
* 读取Excel为Bean的列表
*
* @param Bean类型
* @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略,从0开始计数
* @param startRowIndex 起始行(包含,从0开始计数)
* @param beanType 每行对应Bean的类型
* @return Map的列表
* @since 4.0.1
*/
public List read(int headerRowIndex, int startRowIndex, Class beanType) {
return read(headerRowIndex, startRowIndex, Integer.MAX_VALUE, beanType);
}
/**
* 读取Excel为Bean的列表
*
* @param Bean类型
* @param headerRowIndex 标题所在行,如果标题行在读取的内容行中间,这行做为数据将忽略,从0开始计数
* @param startRowIndex 起始行(包含,从0开始计数)
* @param endRowIndex 读取结束行(包含,从0开始计数)
* @param beanType 每行对应Bean的类型
* @return Map的列表
*/
public List read(int headerRowIndex, int startRowIndex, int endRowIndex, Class beanType) {
final BeanSheetReader reader = new BeanSheetReader<>(headerRowIndex, startRowIndex, endRowIndex, beanType);
reader.setCellEditor(this.cellEditor);
reader.setIgnoreEmptyRow(this.ignoreEmptyRow);
reader.setHeaderAlias(headerAlias);
return read(reader);
}
/**
* 读取数据为指定类型
*
* @param 读取数据类型
* @param sheetReader {@link SheetReader}实现
* @return 数据读取结果
* @since 5.4.4
*/
public T read(SheetReader sheetReader) {
checkNotClosed();
return Assert.notNull(sheetReader).read(this.sheet);
}
/**
* 读取为文本格式
* 使用{@link ExcelExtractor} 提取Excel内容
*
* @param withSheetName 是否附带sheet名
* @return Excel文本
* @since 4.1.0
*/
public String readAsText(boolean withSheetName) {
return ExcelExtractorUtil.readAsText(this.workbook, withSheetName);
}
/**
* 获取 {@link ExcelExtractor} 对象
*
* @return {@link ExcelExtractor}
* @since 4.1.0
*/
public ExcelExtractor getExtractor() {
return ExcelExtractorUtil.getExtractor(this.workbook);
}
/**
* 读取某一行数据
*
* @param rowIndex 行号,从0开始
* @return 一行数据
* @since 4.0.3
*/
public List readRow(int rowIndex) {
return readRow(this.sheet.getRow(rowIndex));
}
/**
* 读取某个单元格的值
*
* @param x X坐标,从0计数,即列号
* @param y Y坐标,从0计数,即行号
* @return 值,如果单元格无值返回null
* @since 4.0.3
*/
public Object readCellValue(int x, int y) {
return CellUtil.getCellValue(getCell(x, y), this.cellEditor);
}
/**
* 获取Excel写出器
* 在读取Excel并做一定编辑后,获取写出器写出,规则如下:
*
* 1. 当从流中读取时,转换为Writer直接使用Sheet对象,此时修改不会影响源文件,Writer中flush需要指定新的路径。
* 2. 当从文件读取时,直接获取文件及sheet名称,此时可以修改原文件。
*
*
* @return {@link ExcelWriter}
* @since 4.0.6
*/
public ExcelWriter getWriter() {
if(null == this.destFile){
// 非读取文件形式,直接获取sheet操作。
return new ExcelWriter(this.sheet);
}
return ExcelUtil.getWriter(this.destFile, this.sheet.getSheetName());
}
// ------------------------------------------------------------------------------------------------------- Private methods start
/**
* 读取一行
*
* @param row 行
* @return 单元格值列表
*/
private List readRow(Row row) {
return RowUtil.readRow(row, this.cellEditor);
}
/**
* 检查是否未关闭状态
*/
private void checkNotClosed() {
Assert.isFalse(this.isClosed, "ExcelReader has been closed!");
}
/**
* 获取Sheet,如果不存在则关闭{@link Workbook}并抛出异常,解决当sheet不存在时,文件依旧被占用问题
* 见:Issue#I8ZIQC
* @param workbook {@link Workbook},非空
* @param name sheet名称,不存在抛出异常
* @return {@link Sheet}
* @throws IllegalArgumentException workbook为空或sheet不能存在
*/
private static Sheet getSheetOrCloseWorkbook(final Workbook workbook, String name) throws IllegalArgumentException{
Assert.notNull(workbook);
if(null == name){
name = "sheet1";
}
final Sheet sheet = workbook.getSheet(name);
if(null == sheet){
IoUtil.close(workbook);
throw new IllegalArgumentException("Sheet [" + name + "] not exist!");
}
return sheet;
}
/**
* 获取Sheet,如果不存在则关闭{@link Workbook}并抛出异常,解决当sheet不存在时,文件依旧被占用问题
* 见:Issue#I8ZIQC
* @param workbook {@link Workbook},非空
* @param sheetIndex sheet index
* @return {@link Sheet}
* @throws IllegalArgumentException workbook为空或sheet不能存在
*/
private static Sheet getSheetOrCloseWorkbook(final Workbook workbook, final int sheetIndex) throws IllegalArgumentException{
Assert.notNull(workbook);
final Sheet sheet;
try {
sheet = workbook.getSheetAt(sheetIndex);
} catch (final IllegalArgumentException e){
IoUtil.close(workbook);
throw e;
}
if(null == sheet){
IoUtil.close(workbook);
throw new IllegalArgumentException("Sheet at [" + sheetIndex + "] not exist!");
}
return sheet;
}
// ------------------------------------------------------------------------------------------------------- Private methods end
}