All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.hutool.poi.excel.ExcelReader Maven / Gradle / Ivy

There is a newer version: 5.8.33
Show newest version
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 }