修复空行读取空指针异常
This commit is contained in:
parent
a91cb48d2f
commit
6890da107e
60
docs/API.md
Normal file
60
docs/API.md
Normal file
@ -0,0 +1,60 @@
|
||||
# 详细参数介绍
|
||||
## 关于常见类解析
|
||||
* EasyExcel 入口类,用于构建开始各种操作
|
||||
* ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
|
||||
* ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
|
||||
* ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
|
||||
* WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
|
||||
* 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel...sheet()方法之前作用域是整个sheet,之后针对单个sheet
|
||||
## 读
|
||||
### 注解
|
||||
* `ExcelProperty` 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
|
||||
* `ExcelIgnore` 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
|
||||
* `DateTimeFormat` 日期转换,用`String`去接收excel日期格式的数据会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat`
|
||||
* `NumberFormat` 数字转换,用`String`去接收excel数字格式的数据会调用这个注解。里面的`value`参照`java.text.DecimalFormat`
|
||||
### 参数
|
||||
#### 通用参数
|
||||
`ReadWorkbook`,`ReadSheet` 都会有的参数,如果为空,默认使用上级。
|
||||
* `converter` 转换器,默认加载了很多转换器。也可以自定义。
|
||||
* `readListener` 监听器,在读取数据的过程中会不断的调用监听器。
|
||||
* `headRowNumber` 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。
|
||||
* `head` 与`clazz`二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。
|
||||
* `clazz` 与`head`二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。
|
||||
* `autoTrim` 字符串、表头等数据自动trim
|
||||
#### ReadWorkbook(理解成excel对象)参数
|
||||
* `excelType` 当前excel的类型 默认会自动判断
|
||||
* `inputStream` 与`file`二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用`file`参数。因为使用了`inputStream` easyexcel会帮忙创建临时文件,最终还是`file`
|
||||
* `file` 与`inputStream`二选一。读取文件的文件。
|
||||
* `autoCloseStream` 自动关闭流。
|
||||
* `readCache` 默认小于5M用 内存,超过5M会使用 `EhCache`,这里不建议使用这个参数。
|
||||
#### ReadSheet(就是excel的一个Sheet)参数
|
||||
* `sheetNo` 需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet
|
||||
* `sheetName` 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配
|
||||
## 写
|
||||
### 注解
|
||||
* `ExcelProperty` index 指定写到第几列,默认根据成员变量排序。`value`指定写入的名称,默认成员变量的名字,多个`value`可以参照快速开始中的复杂头
|
||||
* `ExcelIgnore` 默认所有字段都会写入excel,这个注解会忽略这个字段
|
||||
* `DateTimeFormat` 日期转换,将`Date`写到excel会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat`
|
||||
* `NumberFormat` 数字转换,用`Number`写excel会调用这个注解。里面的`value`参照`java.text.DecimalFormat`
|
||||
### 参数
|
||||
#### 通用参数
|
||||
`WriteWorkbook`,`WriteSheet` ,`WriteTable`都会有的参数,如果为空,默认使用上级。
|
||||
* `converter` 转换器,默认加载了很多转换器。也可以自定义。
|
||||
* `writeHandler` 写的处理器。可以实现`WorkbookWriteHandler`,`SheetWriteHandler`,`RowWriteHandler`,`CellWriteHandler`,在写入excel的不同阶段会调用
|
||||
* `relativeHeadRowIndex` 距离多少行后开始。也就是开头空几行
|
||||
* `needHead` 是否导出头
|
||||
* `head` 与`clazz`二选一。写入文件的头列表,建议使用class。
|
||||
* `clazz` 与`head`二选一。写入文件的头对应的class,也可以使用注解。
|
||||
* `autoTrim` 字符串、表头等数据自动trim
|
||||
#### WriteWorkbook(理解成excel对象)参数
|
||||
* `excelType` 当前excel的类型 默认`xlsx`
|
||||
* `outputStream` 与`file`二选一。写入文件的流
|
||||
* `file` 与`outputStream`二选一。写入的文件
|
||||
* `templateInputStream` 模板的文件流
|
||||
* `templateFile` 模板文件
|
||||
* `autoCloseStream` 自动关闭流。
|
||||
#### WriteSheet(就是excel的一个Sheet)参数
|
||||
* `sheetNo` 需要写入的编码。默认0
|
||||
* `sheetName` 需要些的Sheet名称,默认同`sheetNo`
|
||||
#### WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
|
||||
* `tableNo` 需要写入的编码。默认0
|
2
pom.xml
2
pom.xml
@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>2.0.0-beta5</version>
|
||||
<version>2.0.0-beta6</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>easyexcel</name>
|
||||
|
||||
|
@ -7,13 +7,8 @@
|
||||
* 单个文件的并发写入、读取
|
||||
* 读取图片
|
||||
* 宏
|
||||
#### 关于常见类解析
|
||||
* EasyExcel 入口类,用于构建开始各种操作
|
||||
* ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
|
||||
* ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
|
||||
* ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
|
||||
* WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
|
||||
* 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel...sheet()方法之前作用域是整个sheet,之后针对单个sheet
|
||||
#### 详细参数介绍
|
||||
有些参数不知道怎么用,或者有些功能不知道用什么参数,参照:[详细参数介绍](/docs/API.md)
|
||||
#### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★Star。
|
||||
### 读
|
||||
DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java)
|
||||
|
@ -22,7 +22,6 @@ import com.alibaba.excel.constant.ExcelXmlConstants;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
|
||||
import com.alibaba.excel.util.BooleanUtils;
|
||||
import com.alibaba.excel.util.PositionUtils;
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
@ -36,7 +35,6 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
|
||||
private final AnalysisContext analysisContext;
|
||||
private String currentTag;
|
||||
private String currentCellIndex;
|
||||
private int curRow;
|
||||
private int curCol;
|
||||
private Map<Integer, CellData> curRowContent = new TreeMap<Integer, CellData>();
|
||||
private CellData currentCellData;
|
||||
@ -67,12 +65,6 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
|
||||
// start a cell
|
||||
if (CELL_TAG.equals(name)) {
|
||||
currentCellIndex = attributes.getValue(ExcelXmlConstants.POSITION);
|
||||
int nextRow = PositionUtils.getRow(currentCellIndex);
|
||||
if (nextRow > curRow) {
|
||||
curRow = nextRow;
|
||||
}
|
||||
analysisContext
|
||||
.readRowHolder(new ReadRowHolder(curRow, analysisContext.readSheetHolder().getGlobalConfiguration()));
|
||||
curCol = PositionUtils.getCol(currentCellIndex);
|
||||
|
||||
// t="s" ,it's means String
|
||||
|
@ -6,8 +6,10 @@ import org.xml.sax.Attributes;
|
||||
|
||||
import com.alibaba.excel.analysis.v07.XlsxCellHandler;
|
||||
import com.alibaba.excel.analysis.v07.XlsxRowResultHolder;
|
||||
import com.alibaba.excel.constant.ExcelXmlConstants;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.read.listener.event.EachRowAnalysisFinishEvent;
|
||||
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
|
||||
|
||||
/**
|
||||
* Cell Handler
|
||||
@ -29,7 +31,11 @@ public class ProcessResultCellHandler implements XlsxCellHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startHandle(String name, Attributes attributes) {}
|
||||
public void startHandle(String name, Attributes attributes) {
|
||||
analysisContext
|
||||
.readRowHolder(new ReadRowHolder(Integer.valueOf(attributes.getValue(ExcelXmlConstants.POSITION)),
|
||||
analysisContext.readSheetHolder().getGlobalConfiguration()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endHandle(String name) {
|
||||
|
@ -24,7 +24,7 @@ public @interface NumberFormat {
|
||||
|
||||
/**
|
||||
*
|
||||
* Specific format reference {@link org.apache.commons.math3.fraction.BigFractionFormat}
|
||||
* Specific format reference {@link java.text.DecimalFormat}
|
||||
*
|
||||
* @return Format pattern
|
||||
*/
|
||||
|
@ -6,14 +6,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.converters.ConverterKeyBuild;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.enums.HeadKindEnum;
|
||||
import com.alibaba.excel.event.AbstractIgnoreExceptionReadListener;
|
||||
import com.alibaba.excel.exception.ExcelDataConvertException;
|
||||
import com.alibaba.excel.metadata.CellData;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.Head;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
import com.alibaba.excel.read.metadata.holder.ReadHolder;
|
||||
|
@ -13,7 +13,7 @@ public class ReadRowHolder implements Holder {
|
||||
/**
|
||||
* Returns row index of a row in the sheet that contains this cell.Start form 0.
|
||||
*/
|
||||
private int rowIndex;
|
||||
private Integer rowIndex;
|
||||
|
||||
/**
|
||||
* The result of the previous listener
|
||||
@ -24,7 +24,7 @@ public class ReadRowHolder implements Holder {
|
||||
*/
|
||||
private GlobalConfiguration globalConfiguration;
|
||||
|
||||
public ReadRowHolder(int rowIndex, GlobalConfiguration globalConfiguration) {
|
||||
public ReadRowHolder(Integer rowIndex, GlobalConfiguration globalConfiguration) {
|
||||
this.rowIndex = rowIndex;
|
||||
this.globalConfiguration = globalConfiguration;
|
||||
}
|
||||
@ -45,11 +45,11 @@ public class ReadRowHolder implements Holder {
|
||||
this.currentRowAnalysisResult = currentRowAnalysisResult;
|
||||
}
|
||||
|
||||
public int getRowIndex() {
|
||||
public Integer getRowIndex() {
|
||||
return rowIndex;
|
||||
}
|
||||
|
||||
public void setRowIndex(int rowIndex) {
|
||||
public void setRowIndex(Integer rowIndex) {
|
||||
this.rowIndex = rowIndex;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import com.alibaba.excel.context.WriteContext;
|
||||
import com.alibaba.excel.context.WriteContextImpl;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.converters.ConverterKeyBuild;
|
||||
import com.alibaba.excel.enums.HeadKindEnum;
|
||||
import com.alibaba.excel.exception.ExcelDataConvertException;
|
||||
import com.alibaba.excel.exception.ExcelGenerateException;
|
||||
import com.alibaba.excel.metadata.BaseRowModel;
|
||||
@ -194,25 +195,28 @@ public class ExcelBuilderImpl implements ExcelBuilder {
|
||||
WriteHolder currentWriteHolder = context.currentWriteHolder();
|
||||
BeanMap beanMap = BeanMap.create(oneRowData);
|
||||
Set<String> beanMapHandledSet = new HashSet<String>();
|
||||
Map<Integer, Head> headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
|
||||
Map<Integer, ExcelContentProperty> contentPropertyMap =
|
||||
context.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap();
|
||||
int cellIndex = 0;
|
||||
for (Map.Entry<Integer, ExcelContentProperty> entry : contentPropertyMap.entrySet()) {
|
||||
cellIndex = entry.getKey();
|
||||
ExcelContentProperty excelContentProperty = entry.getValue();
|
||||
String name = excelContentProperty.getField().getName();
|
||||
if (!beanMap.containsKey(name)) {
|
||||
continue;
|
||||
// If it's a class it needs to be cast by type
|
||||
if (HeadKindEnum.CLASS.equals(context.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) {
|
||||
Map<Integer, Head> headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap();
|
||||
Map<Integer, ExcelContentProperty> contentPropertyMap =
|
||||
context.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap();
|
||||
for (Map.Entry<Integer, ExcelContentProperty> entry : contentPropertyMap.entrySet()) {
|
||||
cellIndex = entry.getKey();
|
||||
ExcelContentProperty excelContentProperty = entry.getValue();
|
||||
String name = excelContentProperty.getField().getName();
|
||||
if (!beanMap.containsKey(name)) {
|
||||
continue;
|
||||
}
|
||||
Head head = headMap.get(cellIndex);
|
||||
beforeCellCreate(row, head, relativeRowIndex);
|
||||
Cell cell = WorkBookUtil.createCell(row, cellIndex);
|
||||
Object value = beanMap.get(name);
|
||||
CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell,
|
||||
value, excelContentProperty);
|
||||
afterCellCreate(head, cellData, cell, relativeRowIndex);
|
||||
beanMapHandledSet.add(name);
|
||||
}
|
||||
Head head = headMap.get(cellIndex);
|
||||
beforeCellCreate(row, head, relativeRowIndex);
|
||||
Cell cell = WorkBookUtil.createCell(row, cellIndex);
|
||||
Object value = beanMap.get(name);
|
||||
CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell,
|
||||
value, excelContentProperty);
|
||||
afterCellCreate(head, cellData, cell, relativeRowIndex);
|
||||
beanMapHandledSet.add(name);
|
||||
}
|
||||
// Finish
|
||||
if (beanMapHandledSet.size() == beanMap.size()) {
|
||||
|
@ -214,7 +214,6 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
|
||||
return;
|
||||
}
|
||||
Map<Integer, Head> headMap = getExcelWriteHeadProperty().getHeadMap();
|
||||
Map<Integer, ExcelContentProperty> contentPropertyMap = getExcelWriteHeadProperty().getContentPropertyMap();
|
||||
|
||||
boolean hasColumnWidth = false;
|
||||
for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {
|
||||
@ -227,10 +226,10 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
|
||||
if (hasColumnWidth) {
|
||||
dealColumnWidth(handlerList);
|
||||
}
|
||||
dealRowHigh(handlerList, contentPropertyMap);
|
||||
dealRowHigh(handlerList);
|
||||
}
|
||||
|
||||
private void dealRowHigh(List<WriteHandler> handlerList, Map<Integer, ExcelContentProperty> contentPropertyMap) {
|
||||
private void dealRowHigh(List<WriteHandler> handlerList) {
|
||||
RowHeightProperty headRowHeightProperty = getExcelWriteHeadProperty().getHeadRowHeightProperty();
|
||||
RowHeightProperty contentRowHeightProperty = getExcelWriteHeadProperty().getContentRowHeightProperty();
|
||||
if (headRowHeightProperty == null && contentRowHeightProperty == null) {
|
||||
|
@ -0,0 +1,35 @@
|
||||
package com.alibaba.easyexcel.test.temp.simple;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* 模板的读取类
|
||||
*
|
||||
* @author Jiaju Zhuang
|
||||
*/
|
||||
public class HgListener extends AnalysisEventListener<Map<Integer, String>> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HgListener.class);
|
||||
/**
|
||||
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
|
||||
*/
|
||||
private static final int BATCH_COUNT = 5;
|
||||
|
||||
@Override
|
||||
public void invoke(Map<Integer, String> data, AnalysisContext context) {
|
||||
LOGGER.info("index:{}", context.readRowHolder().getRowIndex());
|
||||
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
LOGGER.info("所有数据解析完成!");
|
||||
}
|
||||
|
||||
}
|
@ -24,10 +24,16 @@ public class HgTest {
|
||||
@Test
|
||||
public void hh() throws IOException {
|
||||
List<Object> list =
|
||||
EasyExcel.read(new FileInputStream("D:\\test\\20190906192340.xlsx")).headRowNumber(0).sheet().doReadSync();
|
||||
EasyExcel.read(new FileInputStream("D:\\test\\商户不匹配工单信息收集表格.xlsx")).headRowNumber(0).sheet().doReadSync();
|
||||
for (Object data : list) {
|
||||
LOGGER.info("返回数据:{}", JSON.toJSONString(data));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hh2() throws IOException {
|
||||
EasyExcel.read(new FileInputStream("D:\\test\\商户不匹配工单信息收集表格.xlsx"))
|
||||
.registerReadListener(new HgListener()).headRowNumber(0).sheet().doRead();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,22 +30,36 @@ public class Wirte {
|
||||
@Test
|
||||
public void simpleWrite() {
|
||||
// 写法1
|
||||
String fileName = TestFileUtil.getPath() + "ttttttttt11" + System.currentTimeMillis() + ".xlsx";
|
||||
String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx";
|
||||
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
|
||||
// 如果这里想使用03 则 传入excelType参数即可
|
||||
EasyExcel.write(fileName).sheet("模板").doWrite(data());
|
||||
EasyExcel.write(fileName).sheet("模板").head(head()).doWrite(data());
|
||||
}
|
||||
|
||||
private List<List<Object>> data() {
|
||||
List<List<Object>> list = new ArrayList<List<Object>>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
List<Object> list1 = new ArrayList<Object>();
|
||||
private List<List<String>> head() {
|
||||
List<List<String>> list = new ArrayList<List<String>>();
|
||||
List<String> head0 = new ArrayList<String>();
|
||||
head0.add("字符串" + System.currentTimeMillis());
|
||||
List<String> head1 = new ArrayList<String>();
|
||||
head1.add("数字" + System.currentTimeMillis());
|
||||
List<String> head2 = new ArrayList<String>();
|
||||
head2.add("日期" + System.currentTimeMillis());
|
||||
list.add(head0);
|
||||
list.add(head1);
|
||||
list.add(head2);
|
||||
return list;
|
||||
}
|
||||
|
||||
list1.add("字符串" + i);
|
||||
list1.add(new Date());
|
||||
list1.add(0.56);
|
||||
list.add(list1);
|
||||
private List<DemoData> data() {
|
||||
List<DemoData> list = new ArrayList<DemoData>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
DemoData data = new DemoData();
|
||||
data.setString("字符串" + i);
|
||||
data.setDate(new Date());
|
||||
data.setDoubleData(0.56);
|
||||
list.add(data);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user