Browse Source

新增读取返回头数据

developing
Jiaju Zhuang 5 years ago
parent
commit
a6f20b8a0f
  1. 91
      quickstart.md
  2. 17
      src/main/java/com/alibaba/excel/event/AnalysisEventListener.java
  3. 37
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  4. 13
      src/main/java/com/alibaba/excel/read/listener/ReadListener.java
  5. 57
      src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java
  6. 87
      src/main/java/com/alibaba/excel/util/ConverterUtils.java
  7. 7
      src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataListener.java
  8. 73
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java
  9. 34
      src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java
  10. 1
      update.md

91
quickstart.md

@ -7,6 +7,12 @@
* 单个文件的并发写入、读取
* 读取图片
* 宏
#### 关于常见类解析
* EasyExcel 入口类,用于构建开始各种操作
* ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
* ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
* ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
* WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
#### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★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)
@ -16,7 +22,10 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
* [日期、数字或者自定义格式转换](#converterRead)
* [多行头](#complexHeaderRead)
* [同步的返回](#synchronousRead)
* [读取表头数据](#synchronousRead)
* [数据转换等异常处理](#exceptionRead)
* [web中的读](#webRead)
### 写
DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java)
* [最简单的写](#simpleWrite)
@ -331,6 +340,88 @@ public class CustomStringStringConverter implements Converter<String> {
}
```
### <span id="headerRead" />同步的返回
##### excel示例
参照:[excel示例](#simpleReadExcel)
##### 对象
参照:[对象](#simpleReadObject)
##### 监听器
参照:[监听器](#simpleReadListener)
里面多了一个方法,只要重写invokeHeadMap方法即可
```java
/**
* 这里会一行行的返回头
*
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
```
##### 代码
```java
/**
* 读取表头数据
*
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void headerRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
}
```
### <span id="exceptionRead" />同步的返回
##### excel示例
参照:[excel示例](#simpleReadExcel)
##### 对象
参照:[对象](#simpleReadObject)
##### 监听器
参照:[监听器](#simpleReadListener)
里面多了一个方法,只要重写onException方法即可
```java
/**
* 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行", exception);
}
```
##### 代码
```java
/**
* 数据转换等异常处理
*
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void exceptionRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
}
```
### <span id="webRead" />web中的读
##### 示例代码
DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java)

17
src/main/java/com/alibaba/excel/event/AnalysisEventListener.java

@ -1,7 +1,11 @@
package com.alibaba.excel.event;
import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
/**
* Receives the return of each piece of data parsed
@ -10,6 +14,19 @@ import com.alibaba.excel.read.listener.ReadListener;
*/
public abstract class AnalysisEventListener<T> implements ReadListener<T> {
@Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context);
}
/**
* Returns the header as a map.Override the current method to receive header data.
*
* @param headMap
* @param context
*/
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {}
/**
* All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
* entire read will terminate.

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

@ -18,6 +18,7 @@ import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.read.metadata.holder.ReadHolder;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.alibaba.excel.util.ConverterUtils;
import net.sf.cglib.beans.BeanMap;
@ -28,6 +29,9 @@ import net.sf.cglib.beans.BeanMap;
*/
public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener<Map<Integer, CellData>> {
@Override
public void invokeHead(Map<Integer, CellData> cellDataMap, AnalysisContext context) {}
@Override
public void invoke(Map<Integer, CellData> cellDataMap, AnalysisContext context) {
ReadHolder currentReadHolder = context.currentReadHolder();
@ -48,7 +52,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
map.put(entry.getKey(), null);
continue;
}
map.put(entry.getKey(), (String)convertValue(cellData, String.class, null,
map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, String.class, null,
currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()));
}
return map;
@ -61,8 +65,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
list.add(null);
continue;
}
list.add((String)convertValue(cellData, String.class, null, currentReadHolder.converterMap(),
currentReadHolder.globalConfiguration()));
list.add((String)ConverterUtils.convertToJavaObject(cellData, String.class, null,
currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()));
}
return list;
}
@ -90,8 +94,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
continue;
}
ExcelContentProperty excelContentProperty = contentPropertyMap.get(index);
Object value = convertValue(cellData, excelContentProperty.getField().getType(), excelContentProperty,
currentReadHolder.converterMap(), currentReadHolder.globalConfiguration());
Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField().getType(),
excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration());
if (value != null) {
map.put(excelContentProperty.getField().getName(), value);
}
@ -100,29 +104,6 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
return resultModel;
}
private Object convertValue(CellData cellData, Class clazz, ExcelContentProperty contentProperty,
Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration) {
if (clazz == CellData.class) {
return new CellData(cellData);
}
Converter converter = null;
if (contentProperty != null) {
converter = contentProperty.getConverter();
}
if (converter == null) {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
}
if (converter == null) {
throw new ExcelDataConvertException(
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
}
try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to " + clazz + " error ", e);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}

13
src/main/java/com/alibaba/excel/read/listener/ReadListener.java

@ -1,7 +1,10 @@
package com.alibaba.excel.read.listener;
import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.Listener;
import com.alibaba.excel.metadata.CellData;
/**
* Interface to listen for read results
@ -20,7 +23,15 @@ public interface ReadListener<T> extends Listener {
void onException(Exception exception, AnalysisContext context) throws Exception;
/**
* when analysis one row trigger invoke function.
* When analysis one head row trigger invoke function.
*
* @param headMap
* @param context
*/
void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context);
/**
* When analysis one row trigger invoke function.
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}

57
src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java

@ -26,6 +26,7 @@ import com.alibaba.excel.read.listener.ReadListenerRegistryCenter;
import com.alibaba.excel.read.listener.event.AnalysisFinishEvent;
import com.alibaba.excel.read.metadata.ReadBasicParameter;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.StringUtils;
/**
@ -118,8 +119,11 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
Map<Integer, CellData> cellDataMap = event.getAnalysisResult();
ReadRowHolder readRowHolder = analysisContext.readRowHolder();
readRowHolder.setCurrentRowAnalysisResult(cellDataMap);
int rowIndex = readRowHolder.getRowIndex();
int headRowNumber = analysisContext.readSheetHolder().getHeadRowNumber();
if (readRowHolder.getRowIndex() >= analysisContext.readSheetHolder().getHeadRowNumber()) {
if (rowIndex >= headRowNumber) {
// Now is data
for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {
try {
readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext);
@ -136,11 +140,29 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
throw new ExcelAnalysisStopException();
}
}
return;
} else {
// Last head column
if (headRowNumber == rowIndex + 1) {
buildHead(analysisContext, cellDataMap);
}
// Now is header
if (analysisContext.readSheetHolder().getHeadRowNumber().equals(readRowHolder.getRowIndex() + 1)) {
buildHead(analysisContext, cellDataMap);
for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) {
try {
readListener.invokeHead(cellDataMap, analysisContext);
} catch (Exception e) {
for (ReadListener readListenerException : analysisContext.currentReadHolder().readListenerList()) {
try {
readListenerException.onException(e, analysisContext);
} catch (Exception exception) {
throw new ExcelAnalysisException("Listen error!", exception);
}
}
}
if (!readListener.hasNext(analysisContext)) {
throw new ExcelAnalysisStopException();
}
}
}
}
@ -155,7 +177,8 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) {
return;
}
Map<Integer, String> dataMap = buildStringMap(cellDataMap, analysisContext.currentReadHolder());
Map<Integer, String> dataMap =
ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder());
ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty();
Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap();
Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap();
@ -192,30 +215,6 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
excelHeadPropertyData.setContentPropertyMap(tmpContentPropertyMap);
}
private Map<Integer, String> buildStringMap(Map<Integer, CellData> cellDataMap, ReadHolder readHolder) {
Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
CellData cellData = entry.getValue();
if (cellData.getType() == CellDataTypeEnum.EMPTY) {
stringMap.put(entry.getKey(), null);
continue;
}
Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) {
throw new ExcelDataConvertException(
"Converter not found, convert " + cellData.getType() + " to String");
}
try {
stringMap.put(entry.getKey(),
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration())));
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to String error ", e);
}
}
return stringMap;
}
public List<ReadListener> getReadListenerList() {
return readListenerList;
}

87
src/main/java/com/alibaba/excel/util/ConverterUtils.java

@ -0,0 +1,87 @@
package com.alibaba.excel.util;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.read.metadata.holder.ReadHolder;
/**
* Converting objects
*
* @author Jiaju Zhuang
**/
public class ConverterUtils {
private ConverterUtils() {}
/**
* Convert it into a String map
*
* @param cellDataMap
* @param readHolder
* @return
*/
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, ReadHolder readHolder) {
Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
CellData cellData = entry.getValue();
if (cellData.getType() == CellDataTypeEnum.EMPTY) {
stringMap.put(entry.getKey(), null);
continue;
}
Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) {
throw new ExcelDataConvertException(
"Converter not found, convert " + cellData.getType() + " to String");
}
try {
stringMap.put(entry.getKey(),
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration())));
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to String error ", e);
}
}
return stringMap;
}
/**
* Convert it into a Java object
*
* @param cellData
* @param clazz
* @param contentProperty
* @param converterMap
* @param globalConfiguration
* @return
*/
public static Object convertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty,
Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration) {
if (clazz == CellData.class) {
return new CellData(cellData);
}
Converter converter = null;
if (contentProperty != null) {
converter = contentProperty.getConverter();
}
if (converter == null) {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
}
if (converter == null) {
throw new ExcelDataConvertException(
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
}
try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to " + clazz + " error ", e);
}
}
}

7
src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataListener.java

@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.core.simple;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.slf4j.Logger;
@ -18,6 +19,12 @@ public class SimpleDataListener extends AnalysisEventListener<SimpleData> {
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataListener.class);
List<SimpleData> list = new ArrayList<SimpleData>();
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.debug("Head is:{}", JSON.toJSONString(headMap));
Assert.assertEquals(headMap.get(0), "姓名");
}
@Override
public void invoke(SimpleData data, AnalysisContext context) {
list.add(data);

73
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java

@ -0,0 +1,73 @@
package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
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 DemoHeadDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoHeadDataListener.class);
/**
* 每隔5条存储数据库实际使用中可以3000条然后清理list 方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行", exception);
}
/**
* 这里会一行行的返回头
*
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
LOGGER.info("存储数据库成功!");
}
}

34
src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java

@ -134,6 +134,40 @@ public class ReadTest {
.headRowNumber(1).doRead();
}
/**
* 读取表头数据
*
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认异步读取excel所以需要创建excel一行一行的回调监听器参照{@link DemoHeadDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void headerRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
}
/**
* 数据转换等异常处理
*
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认异步读取excel所以需要创建excel一行一行的回调监听器参照{@link DemoHeadDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void exceptionRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
}
/**
* 同步的返回不推荐使用如果数据量大会把数据放到内存里面
*/

1
update.md

@ -1,5 +1,6 @@
# 2.0.0-beta3
* 导出完成移除临时目录 [Issue #386](https://github.com/alibaba/easyexcel/issues/386)
* 新增读取返回头数据
# 2.0.0-beta2
* 加速gc回收 [Issue #511](https://github.com/alibaba/easyexcel/issues/511)

Loading…
Cancel
Save