Browse Source

修复空行读取空指针异常

bugfix
Jiaju Zhuang 5 years ago
parent
commit
6890da107e
  1. 60
      docs/API.md
  2. 2
      pom.xml
  3. 9
      quickstart.md
  4. 8
      src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java
  5. 8
      src/main/java/com/alibaba/excel/analysis/v07/handlers/ProcessResultCellHandler.java
  6. 2
      src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java
  7. 3
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  8. 8
      src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java
  9. 38
      src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
  10. 5
      src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java
  11. 35
      src/test/java/com/alibaba/easyexcel/test/temp/simple/HgListener.java
  12. 8
      src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java
  13. 34
      src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java
  14. 4
      update.md

60
docs/API.md

@ -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

@ -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>

9
quickstart.md

@ -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)

8
src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.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

8
src/main/java/com/alibaba/excel/analysis/v07/handlers/ProcessResultCellHandler.java

@ -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) {

2
src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java

@ -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
*/

3
src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java

@ -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;

8
src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java

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

38
src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java

@ -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()) {

5
src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java

@ -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) {

35
src/test/java/com/alibaba/easyexcel/test/temp/simple/HgListener.java

@ -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("所有数据解析完成!");
}
}

8
src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java

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

34
src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java

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

4
update.md

@ -1,3 +1,7 @@
# 2.0.0-beta6
* 修复空行读取空指针异常
* 修复写入指定头为List<List<String>>,但是数据用List<Class>导致的空指针
# 2.0.0-beta5
* 修复在读取值的时候读取了额外值导致数据转换异常

Loading…
Cancel
Save