Browse Source

Merge pull request #2180 from alibaba/developing

Developing
pull/2190/head v3.0.5
Jiaju Zhuang 3 years ago committed by GitHub
parent
commit
f8fd5f0427
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      README.md
  2. 2
      pom.xml
  3. 28
      src/main/java/com/alibaba/excel/event/AnalysisEventListener.java
  4. 23
      src/main/java/com/alibaba/excel/read/listener/IgnoreExceptionReadListener.java
  5. 2
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  6. 14
      src/main/java/com/alibaba/excel/read/listener/PageReadListener.java
  7. 4
      src/main/java/com/alibaba/excel/read/listener/ReadListener.java
  8. 6
      src/main/java/com/alibaba/excel/util/DateUtils.java
  9. 1
      src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java
  10. 29
      src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataTest.java
  11. 30
      src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionThrowDataListener.java
  12. 13
      src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java
  13. 26
      src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java
  14. 28
      src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterDataListener.java
  15. 28
      src/test/java/com/alibaba/easyexcel/test/demo/read/CustomStringStringConverter.java
  16. 30
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java
  17. 37
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java
  18. 19
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java
  19. 37
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java
  20. 23
      src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java
  21. 23
      src/test/java/com/alibaba/easyexcel/test/demo/read/NoModelDataListener.java
  22. 26
      src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java
  23. 35
      src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java
  24. 15
      src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java
  25. 21
      src/test/java/com/alibaba/easyexcel/test/demo/write/CustomStringStringConverter.java
  26. 39
      src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java
  27. 3
      update.md

4
README.md

@ -41,15 +41,13 @@ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
<version>3.0.4</version> <version>3.0.5</version>
</dependency> </dependency>
``` ```
## 广告位 ## 广告位
### 阿里巴巴新零售事业部招募 ### 阿里巴巴新零售事业部招募
阿里巴巴新零售事业部--诚招JAVA资深开发、技术专家。有意向可以微信联系,简历可以发我邮箱jipengfei.jpf@alibaba-inc.com 阿里巴巴新零售事业部--诚招JAVA资深开发、技术专家。有意向可以微信联系,简历可以发我邮箱jipengfei.jpf@alibaba-inc.com
### 阿里巴巴&滴滴内推
有入职滴滴、阿里需求的可以联系群主帮忙内推(会帮忙审核简历并提出修改意见)
### easyexcel人员招募 ### easyexcel人员招募
由于工作较忙,有意愿做开源的同学可以报名,主要负责群里回答&issue处理,当然也可以做一些PR. 由于工作较忙,有意愿做开源的同学可以报名,主要负责群里回答&issue处理,当然也可以做一些PR.
由于开源没有任何物质回报,然后现在的维护同学也是课余时间维护的,所以想加入的同学需要持之以恒,而不是一时兴起. 由于开源没有任何物质回报,然后现在的维护同学也是课余时间维护的,所以想加入的同学需要持之以恒,而不是一时兴起.

2
pom.xml

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId> <artifactId>easyexcel</artifactId>
<version>3.0.4</version> <version>3.0.5</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>easyexcel</name> <name>easyexcel</name>

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

@ -3,7 +3,6 @@ package com.alibaba.excel.event;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils; import com.alibaba.excel.util.ConverterUtils;
@ -28,31 +27,4 @@ public abstract class AnalysisEventListener<T> implements ReadListener<T> {
*/ */
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {} public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {}
/**
* The current method is called when extra information is returned
*
* @param extra
* extra information
* @param context
* analysis context
*/
@Override
public void extra(CellExtra extra, 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.
*
* @param exception
* @param context
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
throw exception;
}
@Override
public boolean hasNext(AnalysisContext context) {
return true;
}
} }

23
src/main/java/com/alibaba/excel/read/listener/IgnoreExceptionReadListener.java

@ -0,0 +1,23 @@
package com.alibaba.excel.read.listener;
import com.alibaba.excel.context.AnalysisContext;
/**
* Interface to listen for read results
*
* @author Jiaju Zhuang
*/
public interface IgnoreExceptionReadListener<T> extends ReadListener<T> {
/**
* All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
* entire read will terminate.
*
* @param exception
* @param context
* @throws Exception
*/
@Override
default void onException(Exception exception, AnalysisContext context) throws Exception {}
}

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

@ -23,7 +23,7 @@ import org.apache.commons.collections4.CollectionUtils;
* *
* @author jipengfei * @author jipengfei
*/ */
public class ModelBuildEventListener implements ReadListener<Map<Integer, ReadCellData<?>>> { public class ModelBuildEventListener implements IgnoreExceptionReadListener<Map<Integer, ReadCellData<?>>> {
@Override @Override
public void invokeHead(Map<Integer, ReadCellData<?>> cellDataMap, AnalysisContext context) { public void invokeHead(Map<Integer, ReadCellData<?>> cellDataMap, AnalysisContext context) {

14
src/main/java/com/alibaba/excel/read/listener/PageReadListener.java

@ -21,7 +21,7 @@ public class PageReadListener<T> implements ReadListener<T> {
/** /**
* Temporary storage of data * Temporary storage of data
*/ */
private List<T> cachedData = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** /**
* consumer * consumer
*/ */
@ -33,17 +33,17 @@ public class PageReadListener<T> implements ReadListener<T> {
@Override @Override
public void invoke(T data, AnalysisContext context) { public void invoke(T data, AnalysisContext context) {
cachedData.add(data); cachedDataList.add(data);
if (cachedData.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
consumer.accept(cachedData); consumer.accept(cachedDataList);
cachedData = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
if (CollectionUtils.isNotEmpty(cachedData)) { if (CollectionUtils.isNotEmpty(cachedDataList)) {
consumer.accept(cachedData); consumer.accept(cachedDataList);
} }
} }

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

@ -21,7 +21,9 @@ public interface ReadListener<T> extends Listener {
* @param context * @param context
* @throws Exception * @throws Exception
*/ */
default void onException(Exception exception, AnalysisContext context) throws Exception {} default void onException(Exception exception, AnalysisContext context) throws Exception {
throw exception;
}
/** /**
* When analysis one head row trigger invoke function. * When analysis one head row trigger invoke function.

6
src/main/java/com/alibaba/excel/util/DateUtils.java

@ -216,9 +216,9 @@ public class DateUtils {
isDateCache = MapUtils.newHashMap(); isDateCache = MapUtils.newHashMap();
DATE_THREAD_LOCAL.set(isDateCache); DATE_THREAD_LOCAL.set(isDateCache);
} else { } else {
Boolean isDateCachedData = isDateCache.get(formatIndex); Boolean isDatecachedDataList = isDateCache.get(formatIndex);
if (isDateCachedData != null) { if (isDatecachedDataList != null) {
return isDateCachedData; return isDatecachedDataList;
} }
} }
boolean isDate = isADateFormatUncached(formatIndex, formatString); boolean isDate = isADateFormatUncached(formatIndex, formatString);

1
src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java

@ -49,6 +49,7 @@ public class DefaultWriteHandlerLoader {
break; break;
case CSV: case CSV:
handlerList.add(new DefaultRowWriteHandler()); handlerList.add(new DefaultRowWriteHandler());
handlerList.add(new FillStyleCellWriteHandler());
break; break;
default: default:
break; break;

29
src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataTest.java

@ -9,6 +9,7 @@ import java.util.List;
import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
@ -22,13 +23,17 @@ public class ExceptionDataTest {
private static File file07; private static File file07;
private static File file03; private static File file03;
private static File fileException07;
private static File fileException03;
private static File fileCsv; private static File fileCsv;
@BeforeClass @BeforeClass
public static void init() { public static void init() {
file07 = TestFileUtil.createNewFile("simple07.xlsx"); file07 = TestFileUtil.createNewFile("exception.xlsx");
file03 = TestFileUtil.createNewFile("simple03.xls"); file03 = TestFileUtil.createNewFile("exception.xls");
fileCsv = TestFileUtil.createNewFile("simpleCsv.csv"); fileCsv = TestFileUtil.createNewFile("exception.csv");
fileException07 = TestFileUtil.createNewFile("exceptionThrow.xlsx");
fileException03 = TestFileUtil.createNewFile("exceptionThrow.xls");
} }
@Test @Test
@ -46,6 +51,24 @@ public class ExceptionDataTest {
readAndWrite(fileCsv); readAndWrite(fileCsv);
} }
@Test
public void t11ReadAndWrite07() throws Exception {
readAndWriteException(fileException07);
}
@Test
public void t12ReadAndWrite03() throws Exception {
readAndWriteException(fileException03);
}
private void readAndWriteException(File file) throws Exception {
EasyExcel.write(new FileOutputStream(file), ExceptionData.class).sheet().doWrite(data());
ArithmeticException exception = Assert.assertThrows(ArithmeticException.class, () -> EasyExcel.read(
new FileInputStream(file), ExceptionData.class,
new ExceptionThrowDataListener()).sheet().doRead());
Assert.assertEquals("/ by zero",exception.getMessage());
}
private void readAndWrite(File file) throws Exception { private void readAndWrite(File file) throws Exception {
EasyExcel.write(new FileOutputStream(file), ExceptionData.class).sheet().doWrite(data()); EasyExcel.write(new FileOutputStream(file), ExceptionData.class).sheet().doWrite(data());
EasyExcel.read(new FileInputStream(file), ExceptionData.class, new ExceptionDataListener()).sheet().doRead(); EasyExcel.read(new FileInputStream(file), ExceptionData.class, new ExceptionDataListener()).sheet().doRead();

30
src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionThrowDataListener.java

@ -0,0 +1,30 @@
package com.alibaba.easyexcel.test.core.exception;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Jiaju Zhuang
*/
public class ExceptionThrowDataListener implements ReadListener<ExceptionData> {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionData.class);
List<ExceptionData> list = new ArrayList<ExceptionData>();
@Override
public void invoke(ExceptionData data, AnalysisContext context) {
list.add(data);
if (list.size() == 5) {
int i = 5 / 0;
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}

13
src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java

@ -1,7 +1,6 @@
package com.alibaba.easyexcel.test.demo.fill; package com.alibaba.easyexcel.test.demo.fill;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -11,6 +10,8 @@ import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper; import com.alibaba.excel.write.metadata.fill.FillWrapper;
@ -48,7 +49,7 @@ public class FillTest {
// 方案2 根据Map填充 // 方案2 根据Map填充
fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
// 这里 会填充到第一个sheet, 然后文件流会自动关闭 // 这里 会填充到第一个sheet, 然后文件流会自动关闭
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = MapUtils.newHashMap();
map.put("name", "张三"); map.put("name", "张三");
map.put("number", 5.2); map.put("number", 5.2);
EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
@ -114,7 +115,7 @@ public class FillTest {
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet); excelWriter.fill(data(), fillConfig, writeSheet);
Map<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = MapUtils.newHashMap();
map.put("date", "2019年10月9日13:28:28"); map.put("date", "2019年10月9日13:28:28");
map.put("total", 1000); map.put("total", 1000);
excelWriter.fill(map, writeSheet); excelWriter.fill(map, writeSheet);
@ -150,8 +151,8 @@ public class FillTest {
// list 后面还有个统计 想办法手动写入 // list 后面还有个统计 想办法手动写入
// 这里偷懒直接用list 也可以用对象 // 这里偷懒直接用list 也可以用对象
List<List<String>> totalListList = new ArrayList<List<String>>(); List<List<String>> totalListList = ListUtils.newArrayList();
List<String> totalList = new ArrayList<String>(); List<String> totalList = ListUtils.newArrayList();
totalListList.add(totalList); totalListList.add(totalList);
totalList.add(null); totalList.add(null);
totalList.add(null); totalList.add(null);
@ -227,7 +228,7 @@ public class FillTest {
} }
private List<FillData> data() { private List<FillData> data() {
List<FillData> list = new ArrayList<FillData>(); List<FillData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
FillData fillData = new FillData(); FillData fillData = new FillData();
list.add(fillData); list.add(fillData);

26
src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java

@ -1,48 +1,48 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 读取头 * 读取头
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class CellDataDemoHeadDataListener extends AnalysisEventListener<CellDataReadDemoData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDemoHeadDataListener.class); public class CellDataDemoHeadDataListener implements ReadListener<CellDataReadDemoData> {
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 100; private static final int BATCH_COUNT = 100;
List<CellDataReadDemoData> list = new ArrayList<CellDataReadDemoData>();
private List<CellDataReadDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override @Override
public void invoke(CellDataReadDemoData data, AnalysisContext context) { public void invoke(CellDataReadDemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

28
src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterDataListener.java

@ -1,49 +1,49 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 模板的读取类 * 模板的读取类
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ConverterDataListener extends AnalysisEventListener<ConverterData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(ConverterDataListener.class); public class ConverterDataListener implements ReadListener<ConverterData> {
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<ConverterData> list = new ArrayList<ConverterData>(); private List<ConverterData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override @Override
public void invoke(ConverterData data, AnalysisContext context) { public void invoke(ConverterData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); cachedDataList.add(data);
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

28
src/test/java/com/alibaba/easyexcel/test/demo/read/CustomStringStringConverter.java

@ -1,11 +1,10 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
/** /**
* String and string converter * String and string converter
@ -26,35 +25,22 @@ public class CustomStringStringConverter implements Converter<String> {
/** /**
* 这里读的时候会调用 * 这里读的时候会调用
* *
* @param cellData * @param context
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return * @return
*/ */
@Override @Override
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, public String convertToJavaData(ReadConverterContext<?> context) {
GlobalConfiguration globalConfiguration) { return "自定义:" + context.getReadCellData().getStringValue();
return "自定义:" + cellData.getStringValue();
} }
/** /**
* 这里是写的时候会调用 不用管 * 这里是写的时候会调用 不用管
* *
* @param value
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return * @return
*/ */
@Override @Override
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty, public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
GlobalConfiguration globalConfiguration) { return new WriteCellData<>(context.getValue());
return new WriteCellData<>(value);
} }
} }

30
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java

@ -1,14 +1,13 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 模板的读取类 * 模板的读取类
@ -16,8 +15,9 @@ import org.slf4j.LoggerFactory;
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); public class DemoDataListener implements ReadListener<DemoData> {
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
@ -25,7 +25,7 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
/** /**
* 缓存的数据 * 缓存的数据
*/ */
private List<DemoData> list = new ArrayList<>(BATCH_COUNT); private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** /**
* 假设这个是一个DAO当然有业务逻辑这个也可以是一个service当然如果不用存储这个对象没用 * 假设这个是一个DAO当然有业务逻辑这个也可以是一个service当然如果不用存储这个对象没用
*/ */
@ -53,13 +53,13 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/ */
@Override @Override
public void invoke(DemoData data, AnalysisContext context) { public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
// 存储完成清理 list // 存储完成清理 list
list = new ArrayList<>(BATCH_COUNT); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@ -72,15 +72,15 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库 // 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
demoDAO.save(list); demoDAO.save(cachedDataList);
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

37
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java

@ -1,29 +1,30 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/** /**
* 读取转换异常 * 读取转换异常
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(DemoExceptionListener.class); public class DemoExceptionListener implements ReadListener<ExceptionDemoData> {
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<ExceptionDemoData> list = new ArrayList<ExceptionDemoData>();
private List<ExceptionDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** /**
* 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行 * 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
@ -34,12 +35,12 @@ public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoDa
*/ */
@Override @Override
public void onException(Exception exception, AnalysisContext context) { public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
// 如果是某一个单元格的转换异常 能获取到具体行号 // 如果是某一个单元格的转换异常 能获取到具体行号
// 如果要获取头的信息 配合invokeHeadMap使用 // 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) { if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(), log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData()); excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
} }
} }
@ -51,30 +52,30 @@ public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoDa
* @param context * @param context
*/ */
@Override @Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
} }
@Override @Override
public void invoke(ExceptionDemoData data, AnalysisContext context) { public void invoke(ExceptionDemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

19
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java

@ -1,21 +1,20 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellExtra; import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert; import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* 读取单元格的批注 * 读取单元格的批注
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
**/ **/
public class DemoExtraListener extends AnalysisEventListener<DemoExtraData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); public class DemoExtraListener implements ReadListener<DemoExtraData> {
@Override @Override
public void invoke(DemoExtraData data, AnalysisContext context) {} public void invoke(DemoExtraData data, AnalysisContext context) {}
@ -25,18 +24,18 @@ public class DemoExtraListener extends AnalysisEventListener<DemoExtraData> {
@Override @Override
public void extra(CellExtra extra, AnalysisContext context) { public void extra(CellExtra extra, AnalysisContext context) {
LOGGER.info("读取到了一条额外信息:{}", JSON.toJSONString(extra)); log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
switch (extra.getType()) { switch (extra.getType()) {
case COMMENT: case COMMENT:
LOGGER.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(), log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),
extra.getText()); extra.getText());
break; break;
case HYPERLINK: case HYPERLINK:
if ("Sheet1!A1".equals(extra.getText())) { if ("Sheet1!A1".equals(extra.getText())) {
LOGGER.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),
extra.getColumnIndex(), extra.getText()); extra.getColumnIndex(), extra.getText());
} else if ("Sheet2!A1".equals(extra.getText())) { } else if ("Sheet2!A1".equals(extra.getText())) {
LOGGER.info( log.info(
"额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}," "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"
+ "内容是:{}", + "内容是:{}",
extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
@ -46,7 +45,7 @@ public class DemoExtraListener extends AnalysisEventListener<DemoExtraData> {
} }
break; break;
case MERGE: case MERGE:
LOGGER.info( log.info(
"额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}", "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
extra.getLastColumnIndex()); extra.getLastColumnIndex());

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

@ -1,29 +1,29 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 读取头 * 读取头
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class DemoHeadDataListener extends AnalysisEventListener<DemoData> { @Slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(DemoHeadDataListener.class); public class DemoHeadDataListener implements ReadListener<DemoData> {
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>(); private List<ExceptionDemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** /**
* 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行 * 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
@ -34,10 +34,10 @@ public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
*/ */
@Override @Override
public void onException(Exception exception, AnalysisContext context) { public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) { if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(), log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData()); excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
} }
} }
@ -49,30 +49,33 @@ public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
* @param context * @param context
*/ */
@Override @Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
// 如果想转成成 Map<Integer,String>
// 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener
// 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换
} }
@Override @Override
public void invoke(DemoData data, AnalysisContext context) { public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

23
src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java

@ -1,49 +1,48 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 模板的读取类 * 模板的读取类
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@Slf4j
public class IndexOrNameDataListener extends AnalysisEventListener<IndexOrNameData> { public class IndexOrNameDataListener extends AnalysisEventListener<IndexOrNameData> {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexOrNameDataListener.class);
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<IndexOrNameData> list = new ArrayList<IndexOrNameData>(); private List<IndexOrNameData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override @Override
public void invoke(IndexOrNameData data, AnalysisContext context) { public void invoke(IndexOrNameData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); cachedDataList.add(data);
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

23
src/test/java/com/alibaba/easyexcel/test/demo/read/NoModelDataListener.java

@ -1,50 +1,49 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.slf4j.Logger; import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
/** /**
* 直接用map接收数据 * 直接用map接收数据
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@Slf4j
public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> { public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
private static final Logger LOGGER = LoggerFactory.getLogger(NoModelDataListener.class);
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>(); private List<Map<Integer, String>> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override @Override
public void invoke(Map<Integer, String> data, AnalysisContext context) { public void invoke(Map<Integer, String> data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); cachedDataList.add(data);
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

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

@ -1,7 +1,6 @@
package com.alibaba.easyexcel.test.demo.read; package com.alibaba.easyexcel.test.demo.read;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -17,12 +16,12 @@ import com.alibaba.excel.enums.CellExtraTypeEnum;
import com.alibaba.excel.read.listener.PageReadListener; import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* 读的常见写法 * 读的常见写法
@ -30,10 +29,9 @@ import org.slf4j.LoggerFactory;
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@Ignore @Ignore
@Slf4j
public class ReadTest { public class ReadTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ReadTest.class);
/** /**
* 最简单的读 * 最简单的读
* <p> * <p>
@ -52,7 +50,7 @@ public class ReadTest {
// 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行 // 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行
EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> { EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
for (DemoData demoData : dataList) { for (DemoData demoData : dataList) {
LOGGER.info("读取到一条数据{}", JSON.toJSONString(demoData)); log.info("读取到一条数据{}", JSON.toJSONString(demoData));
} }
})).sheet().doRead(); })).sheet().doRead();
@ -68,15 +66,15 @@ public class ReadTest {
/** /**
*临时存储 *临时存储
*/ */
private List<DemoData> cachedData = new ArrayList<>(BATCH_COUNT); private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override @Override
public void invoke(DemoData data, AnalysisContext context) { public void invoke(DemoData data, AnalysisContext context) {
cachedData.add(data); cachedDataList.add(data);
if (cachedData.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
// 存储完成清理 list // 存储完成清理 list
cachedData = new ArrayList<>(BATCH_COUNT); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@ -89,8 +87,8 @@ public class ReadTest {
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", cachedData.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
}).sheet().doRead(); }).sheet().doRead();
@ -304,14 +302,14 @@ public class ReadTest {
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync(); List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (DemoData data : list) { for (DemoData data : list) {
LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); log.info("读取到数据:{}", JSON.toJSONString(data));
} }
// 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync(); List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
for (Map<Integer, String> data : listMap) { for (Map<Integer, String> data : listMap) {
// 返回每条数据的键值对 表示所在的列 和所在列的值 // 返回每条数据的键值对 表示所在的列 和所在列的值
LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); log.info("读取到数据:{}", JSON.toJSONString(data));
} }
} }

35
src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java

@ -1,29 +1,27 @@
package com.alibaba.easyexcel.test.demo.web; package com.alibaba.easyexcel.test.demo.web;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/** /**
* 模板的读取类 * 模板的读取类
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class UploadDataListener extends AnalysisEventListener<UploadData> { @Slf4j
private static final Logger LOGGER = public class UploadDataListener implements ReadListener<UploadData> {
LoggerFactory.getLogger(com.alibaba.easyexcel.test.demo.read.DemoDataListener.class);
/** /**
* 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收 * 每隔5条存储数据库实际使用中可以100条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<UploadData> list = new ArrayList<UploadData>(); private List<UploadData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/** /**
* 假设这个是一个DAO当然有业务逻辑这个也可以是一个service当然如果不用存储这个对象没用 * 假设这个是一个DAO当然有业务逻辑这个也可以是一个service当然如果不用存储这个对象没用
*/ */
@ -46,19 +44,18 @@ public class UploadDataListener extends AnalysisEventListener<UploadData> {
/** /**
* 这个每一条数据解析都会来调用 * 这个每一条数据解析都会来调用
* *
* @param data * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context * @param context
*/ */
@Override @Override
public void invoke(UploadData data, AnalysisContext context) { public void invoke(UploadData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) { if (cachedDataList.size() >= BATCH_COUNT) {
saveData(); saveData();
// 存储完成清理 list // 存储完成清理 list
list.clear(); cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
} }
} }
@ -71,15 +68,15 @@ public class UploadDataListener extends AnalysisEventListener<UploadData> {
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库 // 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); log.info("所有数据解析完成!");
} }
/** /**
* 加上存储数据库 * 加上存储数据库
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); log.info("{}条数据,开始存储数据库!", cachedDataList.size());
uploadDAO.save(list); uploadDAO.save(cachedDataList);
LOGGER.info("存储数据库成功!"); log.info("存储数据库成功!");
} }
} }

15
src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java

@ -2,15 +2,15 @@ package com.alibaba.easyexcel.test.demo.web;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -49,12 +49,7 @@ public class WebTest {
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20"); String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
ArrayList<ArrayList<String>> list = new ArrayList<>(); EasyExcel.write(response.getOutputStream()).sheet("模板").doWrite(data());
ArrayList<String> sublist = new ArrayList<String>();
sublist.add("t1");
sublist.add("t2");
list.add(sublist);
EasyExcel.write(response.getOutputStream()).sheet("模板").doWrite(list);
} }
/** /**
@ -79,7 +74,7 @@ public class WebTest {
response.reset(); response.reset();
response.setContentType("application/json"); response.setContentType("application/json");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>(); Map<String, String> map = MapUtils.newHashMap();
map.put("status", "failure"); map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage()); map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map)); response.getWriter().println(JSON.toJSONString(map));
@ -103,7 +98,7 @@ public class WebTest {
} }
private List<DownloadData> data() { private List<DownloadData> data() {
List<DownloadData> list = new ArrayList<DownloadData>(); List<DownloadData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
DownloadData data = new DownloadData(); DownloadData data = new DownloadData();
data.setString("字符串" + 0); data.setString("字符串" + 0);

21
src/test/java/com/alibaba/easyexcel/test/demo/write/CustomStringStringConverter.java

@ -1,11 +1,10 @@
package com.alibaba.easyexcel.test.demo.write; package com.alibaba.easyexcel.test.demo.write;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
/** /**
* String and string converter * String and string converter
@ -26,29 +25,21 @@ public class CustomStringStringConverter implements Converter<String> {
/** /**
* 这里是读的时候会调用 不用管 * 这里是读的时候会调用 不用管
* *
* @param cellData NotNull
* @param contentProperty Nullable
* @param globalConfiguration NotNull
* @return * @return
*/ */
@Override @Override
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, public String convertToJavaData(ReadConverterContext<?> context) {
GlobalConfiguration globalConfiguration) { return context.getReadCellData().getStringValue();
return cellData.getStringValue();
} }
/** /**
* 这里是写的时候会调用 不用管 * 这里是写的时候会调用 不用管
* *
* @param value NotNull
* @param contentProperty Nullable
* @param globalConfiguration NotNull
* @return * @return
*/ */
@Override @Override
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty, public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
GlobalConfiguration globalConfiguration) { return new WriteCellData<>("自定义:" + context.getValue());
return new WriteCellData<>("自定义:" + value);
} }
} }

39
src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java

@ -29,6 +29,7 @@ import com.alibaba.excel.metadata.data.RichTextStringData;
import com.alibaba.excel.metadata.data.WriteCellData; import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.BooleanUtils; import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.merge.LoopMergeStrategy;
@ -263,18 +264,16 @@ public class WriteTest {
@Test @Test
public void imageWrite() throws Exception { public void imageWrite() throws Exception {
String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx";
// 如果使用流 记得关闭
InputStream inputStream = null; String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";
try { try (InputStream inputStream = FileUtils.openInputStream(new File(imagePath))) {
List<ImageDemoData> list = new ArrayList<>(); List<ImageDemoData> list = ListUtils.newArrayList();
ImageDemoData imageDemoData = new ImageDemoData(); ImageDemoData imageDemoData = new ImageDemoData();
list.add(imageDemoData); list.add(imageDemoData);
String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";
// 放入五种类型的图片 实际使用只要选一种即可 // 放入五种类型的图片 实际使用只要选一种即可
imageDemoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); imageDemoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
imageDemoData.setFile(new File(imagePath)); imageDemoData.setFile(new File(imagePath));
imageDemoData.setString(imagePath); imageDemoData.setString(imagePath);
inputStream = FileUtils.openInputStream(new File(imagePath));
imageDemoData.setInputStream(inputStream); imageDemoData.setInputStream(inputStream);
imageDemoData.setUrl(new URL( imageDemoData.setUrl(new URL(
"https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg")); "https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
@ -328,10 +327,6 @@ public class WriteTest {
// 写入数据 // 写入数据
EasyExcel.write(fileName, ImageDemoData.class).sheet().doWrite(list); EasyExcel.write(fileName, ImageDemoData.class).sheet().doWrite(list);
} finally {
if (inputStream != null) {
inputStream.close();
}
} }
} }
@ -733,7 +728,7 @@ public class WriteTest {
} }
private List<LongestMatchColumnWidthData> dataLong() { private List<LongestMatchColumnWidthData> dataLong() {
List<LongestMatchColumnWidthData> list = new ArrayList<>(); List<LongestMatchColumnWidthData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
LongestMatchColumnWidthData data = new LongestMatchColumnWidthData(); LongestMatchColumnWidthData data = new LongestMatchColumnWidthData();
data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i); data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i);
@ -745,12 +740,12 @@ public class WriteTest {
} }
private List<List<String>> variableTitleHead() { private List<List<String>> variableTitleHead() {
List<List<String>> list = new ArrayList<>(); List<List<String>> list = ListUtils.newArrayList();
List<String> head0 = new ArrayList<>(); List<String> head0 = ListUtils.newArrayList();
head0.add("string" + System.currentTimeMillis()); head0.add("string" + System.currentTimeMillis());
List<String> head1 = new ArrayList<>(); List<String> head1 = ListUtils.newArrayList();
head1.add("number" + System.currentTimeMillis()); head1.add("number" + System.currentTimeMillis());
List<String> head2 = new ArrayList<>(); List<String> head2 = ListUtils.newArrayList();
head2.add("date" + System.currentTimeMillis()); head2.add("date" + System.currentTimeMillis());
list.add(head0); list.add(head0);
list.add(head1); list.add(head1);
@ -759,12 +754,12 @@ public class WriteTest {
} }
private List<List<String>> head() { private List<List<String>> head() {
List<List<String>> list = new ArrayList<List<String>>(); List<List<String>> list = ListUtils.newArrayList();
List<String> head0 = new ArrayList<String>(); List<String> head0 = ListUtils.newArrayList();
head0.add("字符串" + System.currentTimeMillis()); head0.add("字符串" + System.currentTimeMillis());
List<String> head1 = new ArrayList<String>(); List<String> head1 = ListUtils.newArrayList();
head1.add("数字" + System.currentTimeMillis()); head1.add("数字" + System.currentTimeMillis());
List<String> head2 = new ArrayList<String>(); List<String> head2 = ListUtils.newArrayList();
head2.add("日期" + System.currentTimeMillis()); head2.add("日期" + System.currentTimeMillis());
list.add(head0); list.add(head0);
list.add(head1); list.add(head1);
@ -773,9 +768,9 @@ public class WriteTest {
} }
private List<List<Object>> dataList() { private List<List<Object>> dataList() {
List<List<Object>> list = new ArrayList<>(); List<List<Object>> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
List<Object> data = new ArrayList<>(); List<Object> data = ListUtils.newArrayList();
data.add("字符串" + i); data.add("字符串" + i);
data.add(new Date()); data.add(new Date());
data.add(0.56); data.add(0.56);
@ -785,7 +780,7 @@ public class WriteTest {
} }
private List<DemoData> data() { private List<DemoData> data() {
List<DemoData> list = new ArrayList<>(); List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
DemoData data = new DemoData(); DemoData data = new DemoData();
data.setString("字符串" + i); data.setString("字符串" + i);

3
update.md

@ -1,3 +1,6 @@
# 3.0.5
* 修复`ReadListener` 转换异常不抛出的问题
# 3.0.4 # 3.0.4
* 调整读写默认大小,防止大批量写的时候可能会full gc * 调整读写默认大小,防止大批量写的时候可能会full gc
* `fill`的情况新增 `afterRowDispose`事件 * `fill`的情况新增 `afterRowDispose`事件

Loading…
Cancel
Save