Browse Source

优化读取全部sheet方案

developing
Jiaju Zhuang 5 years ago
parent
commit
8a2cb69be9
  1. 1
      README.md
  2. 31
      quickstart.md
  3. 1
      src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java
  4. 57
      src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java
  5. 8
      src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  6. 34
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  7. 22
      src/main/java/com/alibaba/excel/util/ConverterUtils.java
  8. 11
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java
  9. 11
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java
  10. 11
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java
  11. 20
      src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java
  12. 12
      src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java
  13. 25
      src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java
  14. 2
      src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java
  15. 1
      src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java
  16. 2
      src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java
  17. 50
      src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java
  18. 22
      src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java
  19. 1
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java
  20. 80
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java
  21. 8
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java
  22. 18
      src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java
  23. 22
      src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java
  24. 2
      src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java
  25. 11
      src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java
  26. 39
      src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java
  27. BIN
      src/test/resources/demo/cellDataDemo.xlsx
  28. 4
      update.md

1
README.md

@ -5,6 +5,7 @@ easyexcel
[![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) [![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html)
[QQ群:662022184](//shang.qq.com/wpa/qunwpa?idkey=53d9d821b0833e3c14670f007488a61e300f00ff4f1b81fd950590d90dd80f80) [QQ群:662022184](//shang.qq.com/wpa/qunwpa?idkey=53d9d821b0833e3c14670f007488a61e300f00ff4f1b81fd950590d90dd80f80)
[钉钉群:21960511](https://qr.dingtalk.com/action/joingroup?code=v1,k1,cchz6k12ci9B08NNqhNRFGXocNVHrZtW0kaOtTKg/Rk=&_dt_no_comment=1&origin=11)
# JAVA解析Excel工具easyexcel # JAVA解析Excel工具easyexcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便

31
quickstart.md

@ -70,6 +70,7 @@ public class DemoData {
``` ```
##### <span id="simpleReadListener" />监听器 ##### <span id="simpleReadListener" />监听器
```java ```java
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> { public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/** /**
@ -113,6 +114,7 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/ */
@Test @Test
public void simpleRead() { public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1: // 写法1:
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
@ -410,12 +412,20 @@ public class CustomStringStringConverter implements Converter<String> {
##### excel示例 ##### excel示例
参照:[excel示例](#simpleReadExcel) 参照:[excel示例](#simpleReadExcel)
##### 对象 ##### 对象
参照:[对象](#simpleReadObject) ```java
@Data
public class ExceptionDemoData {
/**
* 用日期去接字符串 肯定报错
*/
private Date date;
}
```
##### 监听器 ##### 监听器
参照:[监听器](#simpleReadListener) 参照:[监听器](#simpleReadListener)
里面多了一个方法,只要重写onException方法即可 里面多了一个方法,只要重写onException方法即可
```java ```java
/** /**
* 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
* *
* @param exception * @param exception
@ -424,7 +434,14 @@ public class CustomStringStringConverter implements Converter<String> {
*/ */
@Override @Override
public void onException(Exception exception, AnalysisContext context) { public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行", exception); LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
// 如果是某一个单元格的转换异常 能获取到具体行号
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
} }
``` ```
##### 代码 ##### 代码
@ -433,9 +450,9 @@ public class CustomStringStringConverter implements Converter<String> {
* 数据转换等异常处理 * 数据转换等异常处理
* *
* <p> * <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData} * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData}
* <p> * <p>
* 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener}
* <p> * <p>
* 3. 直接读即可 * 3. 直接读即可
*/ */
@ -443,7 +460,7 @@ public class CustomStringStringConverter implements Converter<String> {
public void exceptionRead() { public void exceptionRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet // 这里 需要指定读用哪个class去读,然后读取第一个sheet
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead();
} }
``` ```
@ -1123,7 +1140,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
*/ */
@GetMapping("download") @GetMapping("download")
public void download(HttpServletResponse response) throws IOException { public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应下载的文件名不对。这个时候 请别使用swagger 他会有影响 // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系

1
src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java

@ -92,7 +92,6 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
@Override @Override
public void execute() { public void execute() {
analysisContext.readSheetHolder().getSheetNo();
MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
formatListener = new FormatTrackingHSSFListener(listener); formatListener = new FormatTrackingHSSFListener(listener);
workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener);

57
src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java

@ -1,16 +1,49 @@
package com.alibaba.excel.exception; package com.alibaba.excel.exception;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
/** /**
* Data convert exception * Data convert exception
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
public class ExcelDataConvertException extends RuntimeException { public class ExcelDataConvertException extends RuntimeException {
/**
* NotNull.
*/
private Integer rowIndex;
/**
* NotNull.
*/
private Integer columnIndex;
/**
* Nullable.Only when the header is configured and when the class header is used is not null.
*
* @see {@link ExcelWriterBuilder#head(Class)}
*/
private ExcelContentProperty excelContentProperty;
public ExcelDataConvertException(String message) { public ExcelDataConvertException(String message) {
super(message); super(message);
} }
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message) {
super(message);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message, Throwable cause) {
super(message, cause);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(String message, Throwable cause) { public ExcelDataConvertException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
@ -18,4 +51,28 @@ public class ExcelDataConvertException extends RuntimeException {
public ExcelDataConvertException(Throwable cause) { public ExcelDataConvertException(Throwable cause) {
super(cause); super(cause);
} }
public Integer getRowIndex() {
return rowIndex;
}
public void setRowIndex(Integer rowIndex) {
this.rowIndex = rowIndex;
}
public Integer getColumnIndex() {
return columnIndex;
}
public void setColumnIndex(Integer columnIndex) {
this.columnIndex = columnIndex;
}
public ExcelContentProperty getExcelContentProperty() {
return excelContentProperty;
}
public void setExcelContentProperty(ExcelContentProperty excelContentProperty) {
this.excelContentProperty = excelContentProperty;
}
} }

8
src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java

@ -1,6 +1,7 @@
package com.alibaba.excel.metadata.property; package com.alibaba.excel.metadata.property;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -133,6 +134,13 @@ public class ExcelHeadProperty {
ignoreMap.put(field.getName(), field); ignoreMap.put(field.getName(), field);
continue; continue;
} }
boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field);
continue;
}
if (excelProperty == null || excelProperty.index() < 0) { if (excelProperty == null || excelProperty.index() < 0) {
defaultFieldList.add(field); defaultFieldList.add(field);
continue; continue;

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

@ -33,7 +33,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
public void invoke(Map<Integer, CellData> cellDataMap, AnalysisContext context) { public void invoke(Map<Integer, CellData> cellDataMap, AnalysisContext context) {
ReadHolder currentReadHolder = context.currentReadHolder(); ReadHolder currentReadHolder = context.currentReadHolder();
if (HeadKindEnum.CLASS.equals(currentReadHolder.excelReadHeadProperty().getHeadKind())) { if (HeadKindEnum.CLASS.equals(currentReadHolder.excelReadHeadProperty().getHeadKind())) {
context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder)); context.readRowHolder()
.setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder, context));
return; return;
} }
context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, currentReadHolder, context)); context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, currentReadHolder, context));
@ -41,35 +42,51 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
private Object buildStringList(Map<Integer, CellData> cellDataMap, ReadHolder currentReadHolder, private Object buildStringList(Map<Integer, CellData> cellDataMap, ReadHolder currentReadHolder,
AnalysisContext context) { AnalysisContext context) {
int index = 0;
if (context.readWorkbookHolder().getDefaultReturnMap()) { if (context.readWorkbookHolder().getDefaultReturnMap()) {
Map<Integer, String> map = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1); Map<Integer, String> map = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
while (index < key) {
map.put(index, null);
index++;
}
index++;
if (cellData.getType() == CellDataTypeEnum.EMPTY) { if (cellData.getType() == CellDataTypeEnum.EMPTY) {
map.put(entry.getKey(), null); map.put(key, null);
continue; continue;
} }
map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, null, null, map.put(key,
currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(),
currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key));
} }
return map; return map;
} else { } else {
// Compatible with the old code the old code returns a list // Compatible with the old code the old code returns a list
List<String> list = new ArrayList<String>(); List<String> list = new ArrayList<String>();
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
while (index < key) {
list.add(null);
index++;
}
index++;
if (cellData.getType() == CellDataTypeEnum.EMPTY) { if (cellData.getType() == CellDataTypeEnum.EMPTY) {
list.add(null); list.add(null);
continue; continue;
} }
list.add((String)ConverterUtils.convertToJavaObject(cellData, null, null, list.add(
currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(),
currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key));
} }
return list; return list;
} }
} }
private Object buildUserModel(Map<Integer, CellData> cellDataMap, ReadHolder currentReadHolder) { private Object buildUserModel(Map<Integer, CellData> cellDataMap, ReadHolder currentReadHolder,
AnalysisContext context) {
ExcelReadHeadProperty excelReadHeadProperty = currentReadHolder.excelReadHeadProperty(); ExcelReadHeadProperty excelReadHeadProperty = currentReadHolder.excelReadHeadProperty();
Object resultModel; Object resultModel;
try { try {
@ -92,7 +109,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
} }
ExcelContentProperty excelContentProperty = contentPropertyMap.get(index); ExcelContentProperty excelContentProperty = contentPropertyMap.get(index);
Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField(), Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField(),
excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()); excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration(),
context.readRowHolder().getRowIndex(), index);
if (value != null) { if (value != null) {
map.put(excelContentProperty.getField().getName(), value); map.put(excelContentProperty.getField().getName(), value);
} }

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

@ -63,10 +63,13 @@ public class ConverterUtils {
* @param contentProperty * @param contentProperty
* @param converterMap * @param converterMap
* @param globalConfiguration * @param globalConfiguration
* @param rowIndex
* @param columnIndex
* @return * @return
*/ */
public static Object convertToJavaObject(CellData cellData, Field field, ExcelContentProperty contentProperty, public static Object convertToJavaObject(CellData cellData, Field field, ExcelContentProperty contentProperty,
Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration) { Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex,
Integer columnIndex) {
Class clazz; Class clazz;
if (field == null) { if (field == null) {
clazz = String.class; clazz = String.class;
@ -83,11 +86,12 @@ public class ConverterUtils {
classGeneric = String.class; classGeneric = String.class;
} }
CellData cellDataReturn = new CellData(cellData); CellData cellDataReturn = new CellData(cellData);
cellDataReturn.setData( cellDataReturn.setData(doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap,
doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, globalConfiguration)); globalConfiguration, rowIndex, columnIndex));
return cellDataReturn; return cellDataReturn;
} }
return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, globalConfiguration); return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, globalConfiguration, rowIndex,
columnIndex);
} }
/** /**
@ -97,10 +101,13 @@ public class ConverterUtils {
* @param contentProperty * @param contentProperty
* @param converterMap * @param converterMap
* @param globalConfiguration * @param globalConfiguration
* @param rowIndex
* @param columnIndex
* @return * @return
*/ */
private static Object doConvertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, private static Object doConvertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty,
Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration) { Map<String, Converter> converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex,
Integer columnIndex) {
Converter converter = null; Converter converter = null;
if (contentProperty != null) { if (contentProperty != null) {
converter = contentProperty.getConverter(); converter = contentProperty.getConverter();
@ -109,13 +116,14 @@ public class ConverterUtils {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
} }
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); "Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
} }
try { try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration); return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to " + clazz + " error ", e); throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
"Convert data " + cellData + " to " + clazz + " error ", e);
} }
} }
} }

11
src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java

@ -79,6 +79,17 @@ public class ExcelWriterBuilder {
return this; return this;
} }
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeWorkbook.setUseDefaultStyle(useDefaultStyle);
return this;
}
/** /**
* The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a
* field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed.

11
src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java

@ -74,6 +74,17 @@ public class ExcelWriterSheetBuilder {
return this; return this;
} }
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterSheetBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeSheet.setUseDefaultStyle(useDefaultStyle);
return this;
}
/** /**
* Custom type conversions override the default. * Custom type conversions override the default.
* *

11
src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java

@ -78,6 +78,17 @@ public class ExcelWriterTableBuilder {
return this; return this;
} }
/**
* Use the default style.Default is true.
*
* @param useDefaultStyle
* @return
*/
public ExcelWriterTableBuilder useDefaultStyle(Boolean useDefaultStyle) {
writeTable.setUseDefaultStyle(useDefaultStyle);
return this;
}
/** /**
* Custom type conversions override the default. * Custom type conversions override the default.
* *

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

@ -21,16 +21,18 @@ public class DefaultWriteHandlerLoader {
* *
* @return * @return
*/ */
public static List<WriteHandler> loadDefaultHandler() { public static List<WriteHandler> loadDefaultHandler(Boolean useDefaultStyle) {
List<WriteHandler> handlerList = new ArrayList<WriteHandler>(); List<WriteHandler> handlerList = new ArrayList<WriteHandler>();
WriteCellStyle headWriteCellStyle = new WriteCellStyle(); if (useDefaultStyle) {
headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); WriteCellStyle headWriteCellStyle = new WriteCellStyle();
WriteFont headWriteFont = new WriteFont(); headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
headWriteFont.setFontName("宋体"); WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short)14); headWriteFont.setFontName("宋体");
headWriteFont.setBold(true); headWriteFont.setFontHeightInPoints((short)14);
headWriteCellStyle.setWriteFont(headWriteFont); headWriteFont.setBold(true);
handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList<WriteCellStyle>())); headWriteCellStyle.setWriteFont(headWriteFont);
handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList<WriteCellStyle>()));
}
return handlerList; return handlerList;
} }

12
src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java

@ -24,6 +24,10 @@ public class WriteBasicParameter extends BasicParameter {
* Custom type handler override the default * Custom type handler override the default
*/ */
private List<WriteHandler> customWriteHandlerList = new ArrayList<WriteHandler>(); private List<WriteHandler> customWriteHandlerList = new ArrayList<WriteHandler>();
/**
* Use the default style.Default is true.
*/
private Boolean useDefaultStyle;
public Integer getRelativeHeadRowIndex() { public Integer getRelativeHeadRowIndex() {
return relativeHeadRowIndex; return relativeHeadRowIndex;
@ -48,4 +52,12 @@ public class WriteBasicParameter extends BasicParameter {
public void setCustomWriteHandlerList(List<WriteHandler> customWriteHandlerList) { public void setCustomWriteHandlerList(List<WriteHandler> customWriteHandlerList) {
this.customWriteHandlerList = customWriteHandlerList; this.customWriteHandlerList = customWriteHandlerList;
} }
public Boolean getUseDefaultStyle() {
return useDefaultStyle;
}
public void setUseDefaultStyle(Boolean useDefaultStyle) {
this.useDefaultStyle = useDefaultStyle;
}
} }

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

@ -61,6 +61,10 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
* Write handler for workbook * Write handler for workbook
*/ */
private Map<Class<? extends WriteHandler>, List<WriteHandler>> writeHandlerMap; private Map<Class<? extends WriteHandler>, List<WriteHandler>> writeHandlerMap;
/**
* Use the default style.Default is true.
*/
private Boolean useDefaultStyle;
public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder, public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder,
Boolean convertAllFiled) { Boolean convertAllFiled) {
@ -96,6 +100,16 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
this.relativeHeadRowIndex = writeBasicParameter.getRelativeHeadRowIndex(); this.relativeHeadRowIndex = writeBasicParameter.getRelativeHeadRowIndex();
} }
if (writeBasicParameter.getUseDefaultStyle() == null) {
if (parentAbstractWriteHolder == null) {
this.useDefaultStyle = Boolean.TRUE;
} else {
this.useDefaultStyle = parentAbstractWriteHolder.getUseDefaultStyle();
}
} else {
this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle();
}
// Initialization property // Initialization property
this.excelWriteHeadProperty = new ExcelWriteHeadProperty(getClazz(), getHead(), convertAllFiled); this.excelWriteHeadProperty = new ExcelWriteHeadProperty(getClazz(), getHead(), convertAllFiled);
@ -117,7 +131,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
if (parentAbstractWriteHolder != null) { if (parentAbstractWriteHolder != null) {
parentWriteHandlerMap = parentAbstractWriteHolder.getWriteHandlerMap(); parentWriteHandlerMap = parentAbstractWriteHolder.getWriteHandlerMap();
} else { } else {
handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler()); handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler(useDefaultStyle));
} }
this.writeHandlerMap = sortAndClearUpHandler(handlerList, parentWriteHandlerMap); this.writeHandlerMap = sortAndClearUpHandler(handlerList, parentWriteHandlerMap);
@ -134,6 +148,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
} }
} }
} }
/** /**
@ -360,6 +375,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ
this.relativeHeadRowIndex = relativeHeadRowIndex; this.relativeHeadRowIndex = relativeHeadRowIndex;
} }
public Boolean getUseDefaultStyle() {
return useDefaultStyle;
}
public void setUseDefaultStyle(Boolean useDefaultStyle) {
this.useDefaultStyle = useDefaultStyle;
}
@Override @Override
public ExcelWriteHeadProperty excelWriteHeadProperty() { public ExcelWriteHeadProperty excelWriteHeadProperty() {
return getExcelWriteHeadProperty(); return getExcelWriteHeadProperty();

2
src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java

@ -29,4 +29,6 @@ public class AnnotationData {
private Double number; private Double number;
@ExcelIgnore @ExcelIgnore
private String ignore; private String ignore;
private static final String staticFinal = "test";
private transient String transientString;
} }

1
src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java

@ -48,6 +48,7 @@ public class AnnotationDataTest {
data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); data.setDate(DateUtils.parseDate("2020-01-01 01:01:01"));
data.setNumber(99.99); data.setNumber(99.99);
data.setIgnore("忽略"); data.setIgnore("忽略");
data.setTransientString("忽略");
list.add(data); list.add(data);
return list; return list;
} }

2
src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java

@ -20,7 +20,7 @@ public class ExceptionDataListener extends AnalysisEventListener<ExceptionData>
@Override @Override
public void onException(Exception exception, AnalysisContext context) { public void onException(Exception exception, AnalysisContext context) {
LOGGER.info("抛出异常,忽略:{}", exception.getMessage()); LOGGER.info("抛出异常,忽略:{}", exception.getMessage(), exception);
} }
@Override @Override

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

@ -0,0 +1,50 @@
package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List;
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.excel.metadata.CellData;
import com.alibaba.fastjson.JSON;
/**
* 读取头
*
* @author Jiaju Zhuang
*/
public class CellDataDemoHeadDataListener extends AnalysisEventListener<CellDataReadDemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDemoHeadDataListener.class);
/**
* 每隔5条存储数据库实际使用中可以3000条然后清理list 方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<CellDataReadDemoData> list = new ArrayList<CellDataReadDemoData>();
@Override
public void invoke(CellDataReadDemoData 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("存储数据库成功!");
}
}

22
src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java

@ -0,0 +1,22 @@
package com.alibaba.easyexcel.test.demo.read;
import java.util.Date;
import com.alibaba.excel.metadata.CellData;
import lombok.Data;
/**
* 基础数据类.这里的排序和excel里面的排序一致
*
* @author Jiaju Zhuang
**/
@Data
public class CellDataReadDemoData {
private CellData<String> string;
// 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是number
private CellData<Date> date;
private CellData<Double> doubleData;
// 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复
private CellData<String> formulaValue;
}

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

@ -15,6 +15,7 @@ import com.alibaba.fastjson.JSON;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> { public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/** /**

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

@ -0,0 +1,80 @@
package com.alibaba.easyexcel.test.demo.read;
import java.util.ArrayList;
import java.util.List;
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.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSON;
/**
* 读取转换异常
*
* @author Jiaju Zhuang
*/
public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoExceptionListener.class);
/**
* 每隔5条存储数据库实际使用中可以3000条然后清理list 方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<ExceptionDemoData> list = new ArrayList<ExceptionDemoData>();
/**
* 在转换异常 获取其他异常下会调用本接口抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
// 如果是某一个单元格的转换异常 能获取到具体行号
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
}
/**
* 这里会一行行的返回头
*
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
}
@Override
public void invoke(ExceptionDemoData 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("存储数据库成功!");
}
}

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

@ -10,6 +10,7 @@ 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.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
/** /**
@ -34,7 +35,12 @@ 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); LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
}
} }
/** /**

18
src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java

@ -0,0 +1,18 @@
package com.alibaba.easyexcel.test.demo.read;
import java.util.Date;
import lombok.Data;
/**
* 基础数据类.这里的排序和excel里面的排序一致
*
* @author Jiaju Zhuang
**/
@Data
public class ExceptionDemoData {
/**
* 用日期去接字符串 肯定报错
*/
private Date date;
}

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

@ -39,6 +39,7 @@ public class ReadTest {
*/ */
@Test @Test
public void simpleRead() { public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1: // 写法1:
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
@ -164,7 +165,7 @@ public class ReadTest {
} }
/** /**
* 数据转换等异常处理 * 读取公式和单元格类型
* *
* <p> * <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData} * 1. 创建excel对应的实体对象 参照{@link DemoData}
@ -174,10 +175,27 @@ public class ReadTest {
* 3. 直接读即可 * 3. 直接读即可
*/ */
@Test @Test
public void cellDataRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet
EasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead();
}
/**
* 数据转换等异常处理
*
* <p>
* 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData}
* <p>
* 2. 由于默认异步读取excel所以需要创建excel一行一行的回调监听器参照{@link DemoExceptionListener}
* <p>
* 3. 直接读即可
*/
@Test
public void exceptionRead() { public void exceptionRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet // 这里 需要指定读用哪个class去读,然后读取第一个sheet
EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead();
} }
/** /**

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

@ -36,7 +36,7 @@ public class WebTest {
*/ */
@GetMapping("download") @GetMapping("download")
public void download(HttpServletResponse response) throws IOException { public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应下载的文件名不对。这个时候 请别使用swagger 他会有影响 // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系

11
src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java

@ -3,6 +3,7 @@ package com.alibaba.easyexcel.test.temp;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.util.List; import java.util.List;
import java.util.Map;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -35,4 +36,14 @@ public class LockTest {
} }
} }
@Test
public void test2() throws Exception {
List<Object> list =
EasyExcel.read(new FileInputStream("D:\\test\\null.xlsx")).sheet().headRowNumber(0).doReadSync();
for (Object data : list) {
LOGGER.info("返回数据:{}", ((Map)data).size());
LOGGER.info("返回数据:{}", JSON.toJSONString(data));
}
}
} }

39
src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java

@ -18,6 +18,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.fastjson.JSON;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
@ -30,21 +31,49 @@ import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
public class PoiWriteTest { public class PoiWriteTest {
private static final Logger LOGGER = LoggerFactory.getLogger(PoiWriteTest.class); private static final Logger LOGGER = LoggerFactory.getLogger(PoiWriteTest.class);
@Test
public void write0() throws IOException {
FileOutputStream fileOutputStream =
new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx");
SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook();
SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1");
SXSSFRow row = sheet.createRow(0);
SXSSFCell cell1 = row.createCell(0);
cell1.setCellValue(999999999999999L);
SXSSFCell cell2 = row.createCell(1);
cell2.setCellValue(1000000000000001L);
sxxsFWorkbook.write(fileOutputStream);
}
@Test @Test
public void write() throws IOException { public void write() throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("D://test//tt12.xlsx"); FileOutputStream fileOutputStream =
new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx");
SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook();
SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1");
SXSSFRow row = sheet.createRow(0); SXSSFRow row = sheet.createRow(0);
SXSSFCell cell1 = row.createCell(0); SXSSFCell cell1 = row.createCell(0);
cell1.setCellValue(1); cell1.setCellValue(Long.toString(999999999999999L));
SXSSFCell cell2 = row.createCell(1); SXSSFCell cell2 = row.createCell(1);
cell2.setCellValue(1); cell2.setCellValue(Long.toString(1000000000000001L));
SXSSFCell cell3 = row.createCell(2);
cell3.setCellFormula("=A1+B1");
sxxsFWorkbook.write(fileOutputStream); sxxsFWorkbook.write(fileOutputStream);
} }
@Test
public void write1() throws IOException {
System.out.println(JSON.toJSONString(long2Bytes(-999999999999999L)));
System.out.println(JSON.toJSONString(long2Bytes(-9999999999999999L)));
}
public static byte[] long2Bytes(long num) {
byte[] byteNum = new byte[8];
for (int ix = 0; ix < 8; ++ix) {
int offset = 64 - (ix + 1) * 8;
byteNum[ix] = (byte)((num >> offset) & 0xff);
}
return byteNum;
}
private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$"); private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$");
@Test @Test

BIN
src/test/resources/demo/cellDataDemo.xlsx

Binary file not shown.

4
update.md

@ -2,6 +2,10 @@
* 新增支持导入、导出支持公式 * 新增支持导入、导出支持公式
* 新增支持读取单元格类型、写入指定单元格类型 * 新增支持读取单元格类型、写入指定单元格类型
* 支持通过模板填充数据 * 支持通过模板填充数据
* 新增写支持 禁用头样式 autoHeadStyle
* 用map读取数据 空的单元格也会有个 null的数据
* 转换报错 能获取到对应的行号和列号
* 优化读取全部sheet方案
# 2.0.5 # 2.0.5
* 优化07版超大文件读取方案 * 优化07版超大文件读取方案

Loading…
Cancel
Save