diff --git a/README.md b/README.md index 583cc4b1..bf9871a8 100644 --- a/README.md +++ b/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) [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比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 diff --git a/quickstart.md b/quickstart.md index 728f373d..ffb1f4b8 100644 --- a/quickstart.md +++ b/quickstart.md @@ -70,6 +70,7 @@ public class DemoData { ``` ##### 监听器 ```java +// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 public class DemoDataListener extends AnalysisEventListener { private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); /** @@ -113,6 +114,7 @@ public class DemoDataListener extends AnalysisEventListener { */ @Test public void simpleRead() { + // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 写法1: String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 @@ -410,12 +412,20 @@ public class CustomStringStringConverter implements Converter { ##### excel示例 参照:[excel示例](#simpleReadExcel) ##### 对象 -参照:[对象](#simpleReadObject) +```java +@Data +public class ExceptionDemoData { + /** + * 用日期去接字符串 肯定报错 + */ + private Date date; +} +``` ##### 监听器 参照:[监听器](#simpleReadListener) 里面多了一个方法,只要重写onException方法即可 ```java - /** + /** * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 * * @param exception @@ -424,7 +434,14 @@ public class CustomStringStringConverter implements Converter { */ @Override 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,17 +450,17 @@ public class CustomStringStringConverter implements Converter { * 数据转换等异常处理 * *

- * 1. 创建excel对应的实体对象 参照{@link DemoData} + * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData} *

- * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} + * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} *

* 3. 直接读即可 */ @Test public void exceptionRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + 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") public void download(HttpServletResponse response) throws IOException { - // 这里注意 有同学反应下载的文件名不对。这个时候 请别使用swagger 他会有影响 + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index 7134e6f8..129b74e3 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -92,7 +92,6 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { @Override public void execute() { - analysisContext.readSheetHolder().getSheetNo(); MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); formatListener = new FormatTrackingHSSFListener(listener); workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); diff --git a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java b/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java index 74630f04..4af25690 100644 --- a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java +++ b/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java @@ -1,16 +1,49 @@ package com.alibaba.excel.exception; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; + /** * Data convert exception * * @author Jiaju Zhuang */ 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) { 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) { super(message, cause); } @@ -18,4 +51,28 @@ public class ExcelDataConvertException extends RuntimeException { public ExcelDataConvertException(Throwable 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; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 09d55603..4bffce6d 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -1,6 +1,7 @@ package com.alibaba.excel.metadata.property; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -133,6 +134,13 @@ public class ExcelHeadProperty { ignoreMap.put(field.getName(), field); 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) { defaultFieldList.add(field); continue; diff --git a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java index 6bea5931..a368024f 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -33,7 +33,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener public void invoke(Map cellDataMap, AnalysisContext context) { ReadHolder currentReadHolder = context.currentReadHolder(); if (HeadKindEnum.CLASS.equals(currentReadHolder.excelReadHeadProperty().getHeadKind())) { - context.readRowHolder().setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder)); + context.readRowHolder() + .setCurrentRowAnalysisResult(buildUserModel(cellDataMap, currentReadHolder, context)); return; } context.readRowHolder().setCurrentRowAnalysisResult(buildStringList(cellDataMap, currentReadHolder, context)); @@ -41,35 +42,51 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener private Object buildStringList(Map cellDataMap, ReadHolder currentReadHolder, AnalysisContext context) { + int index = 0; if (context.readWorkbookHolder().getDefaultReturnMap()) { Map map = new HashMap(cellDataMap.size() * 4 / 3 + 1); for (Map.Entry entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); CellData cellData = entry.getValue(); + while (index < key) { + map.put(index, null); + index++; + } + index++; if (cellData.getType() == CellDataTypeEnum.EMPTY) { - map.put(entry.getKey(), null); + map.put(key, null); continue; } - map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, null, null, - currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); + map.put(key, + (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), + currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } return map; } else { // Compatible with the old code the old code returns a list List list = new ArrayList(); for (Map.Entry entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); CellData cellData = entry.getValue(); + while (index < key) { + list.add(null); + index++; + } + index++; if (cellData.getType() == CellDataTypeEnum.EMPTY) { list.add(null); continue; } - list.add((String)ConverterUtils.convertToJavaObject(cellData, null, null, - currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); + list.add( + (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), + currentReadHolder.globalConfiguration(), context.readRowHolder().getRowIndex(), key)); } return list; } } - private Object buildUserModel(Map cellDataMap, ReadHolder currentReadHolder) { + private Object buildUserModel(Map cellDataMap, ReadHolder currentReadHolder, + AnalysisContext context) { ExcelReadHeadProperty excelReadHeadProperty = currentReadHolder.excelReadHeadProperty(); Object resultModel; try { @@ -92,7 +109,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener } ExcelContentProperty excelContentProperty = contentPropertyMap.get(index); Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField(), - excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()); + excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration(), + context.readRowHolder().getRowIndex(), index); if (value != null) { map.put(excelContentProperty.getField().getName(), value); } diff --git a/src/main/java/com/alibaba/excel/util/ConverterUtils.java b/src/main/java/com/alibaba/excel/util/ConverterUtils.java index 1a1e70a5..b6244c23 100644 --- a/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -63,10 +63,13 @@ public class ConverterUtils { * @param contentProperty * @param converterMap * @param globalConfiguration + * @param rowIndex + * @param columnIndex * @return */ public static Object convertToJavaObject(CellData cellData, Field field, ExcelContentProperty contentProperty, - Map converterMap, GlobalConfiguration globalConfiguration) { + Map converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex, + Integer columnIndex) { Class clazz; if (field == null) { clazz = String.class; @@ -83,11 +86,12 @@ public class ConverterUtils { classGeneric = String.class; } CellData cellDataReturn = new CellData(cellData); - cellDataReturn.setData( - doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, globalConfiguration)); + cellDataReturn.setData(doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, + globalConfiguration, rowIndex, columnIndex)); 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 converterMap * @param globalConfiguration + * @param rowIndex + * @param columnIndex * @return */ private static Object doConvertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, - Map converterMap, GlobalConfiguration globalConfiguration) { + Map converterMap, GlobalConfiguration globalConfiguration, Integer rowIndex, + Integer columnIndex) { Converter converter = null; if (contentProperty != null) { converter = contentProperty.getConverter(); @@ -109,13 +116,14 @@ public class ConverterUtils { converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); } if (converter == null) { - throw new ExcelDataConvertException( + throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, "Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); } try { return converter.convertToJavaData(cellData, contentProperty, globalConfiguration); } catch (Exception e) { - throw new ExcelDataConvertException("Convert data " + cellData + " to " + clazz + " error ", e); + throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, + "Convert data " + cellData + " to " + clazz + " error ", e); } } } diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java index 5e699c32..3faad69f 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -79,6 +79,17 @@ public class ExcelWriterBuilder { 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 * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java index 8a9e0c66..26acff51 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -74,6 +74,17 @@ public class ExcelWriterSheetBuilder { 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. * diff --git a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java index 14f755ab..eca1fe07 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java @@ -78,6 +78,17 @@ public class ExcelWriterTableBuilder { 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. * diff --git a/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java b/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java index f52af6b3..dff84d7a 100644 --- a/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java +++ b/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java @@ -21,16 +21,18 @@ public class DefaultWriteHandlerLoader { * * @return */ - public static List loadDefaultHandler() { + public static List loadDefaultHandler(Boolean useDefaultStyle) { List handlerList = new ArrayList(); - WriteCellStyle headWriteCellStyle = new WriteCellStyle(); - headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); - WriteFont headWriteFont = new WriteFont(); - headWriteFont.setFontName("宋体"); - headWriteFont.setFontHeightInPoints((short)14); - headWriteFont.setBold(true); - headWriteCellStyle.setWriteFont(headWriteFont); - handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList())); + if (useDefaultStyle) { + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontName("宋体"); + headWriteFont.setFontHeightInPoints((short)14); + headWriteFont.setBold(true); + headWriteCellStyle.setWriteFont(headWriteFont); + handlerList.add(new HorizontalCellStyleStrategy(headWriteCellStyle, new ArrayList())); + } return handlerList; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java index 3a43ff6a..2689cd98 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java +++ b/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 */ private List customWriteHandlerList = new ArrayList(); + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; public Integer getRelativeHeadRowIndex() { return relativeHeadRowIndex; @@ -48,4 +52,12 @@ public class WriteBasicParameter extends BasicParameter { public void setCustomWriteHandlerList(List customWriteHandlerList) { this.customWriteHandlerList = customWriteHandlerList; } + + public Boolean getUseDefaultStyle() { + return useDefaultStyle; + } + + public void setUseDefaultStyle(Boolean useDefaultStyle) { + this.useDefaultStyle = useDefaultStyle; + } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java index 80d3e7b9..72f5f9be 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java +++ b/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 */ private Map, List> writeHandlerMap; + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder, Boolean convertAllFiled) { @@ -96,6 +100,16 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ 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 this.excelWriteHeadProperty = new ExcelWriteHeadProperty(getClazz(), getHead(), convertAllFiled); @@ -117,7 +131,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ if (parentAbstractWriteHolder != null) { parentWriteHandlerMap = parentAbstractWriteHolder.getWriteHandlerMap(); } else { - handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler()); + handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler(useDefaultStyle)); } this.writeHandlerMap = sortAndClearUpHandler(handlerList, parentWriteHandlerMap); @@ -134,6 +148,7 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); } } + } /** @@ -360,6 +375,14 @@ public abstract class AbstractWriteHolder extends AbstractHolder implements Writ this.relativeHeadRowIndex = relativeHeadRowIndex; } + public Boolean getUseDefaultStyle() { + return useDefaultStyle; + } + + public void setUseDefaultStyle(Boolean useDefaultStyle) { + this.useDefaultStyle = useDefaultStyle; + } + @Override public ExcelWriteHeadProperty excelWriteHeadProperty() { return getExcelWriteHeadProperty(); diff --git a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java index 2a028f3f..f1e91197 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java @@ -29,4 +29,6 @@ public class AnnotationData { private Double number; @ExcelIgnore private String ignore; + private static final String staticFinal = "test"; + private transient String transientString; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java index 5acd5db1..4489f1c1 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java +++ b/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.setNumber(99.99); data.setIgnore("忽略"); + data.setTransientString("忽略"); list.add(data); return list; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java index ffad8387..d9c26e26 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java @@ -20,7 +20,7 @@ public class ExceptionDataListener extends AnalysisEventListener @Override public void onException(Exception exception, AnalysisContext context) { - LOGGER.info("抛出异常,忽略:{}", exception.getMessage()); + LOGGER.info("抛出异常,忽略:{}", exception.getMessage(), exception); } @Override diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java new file mode 100644 index 00000000..896cea62 --- /dev/null +++ b/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 { + private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDemoHeadDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + @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("存储数据库成功!"); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java new file mode 100644 index 00000000..f5d01e0c --- /dev/null +++ b/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; + // 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是number + private CellData date; + private CellData doubleData; + // 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复 + private CellData formulaValue; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java index bf4dd639..434af3b6 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java @@ -15,6 +15,7 @@ import com.alibaba.fastjson.JSON; * * @author Jiaju Zhuang */ +// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 public class DemoDataListener extends AnalysisEventListener { private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); /** diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java new file mode 100644 index 00000000..0f00719d --- /dev/null +++ b/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 { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoExceptionListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + /** + * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 + * + * @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 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("存储数据库成功!"); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java index bc55052b..184409a4 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java +++ b/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.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.fastjson.JSON; /** @@ -34,7 +35,12 @@ public class DemoHeadDataListener extends AnalysisEventListener { */ @Override 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()); + } } /** diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java new file mode 100644 index 00000000..8040728d --- /dev/null +++ b/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; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java index 06ca4b64..3fb514cf 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -39,6 +39,7 @@ public class ReadTest { */ @Test public void simpleRead() { + // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 // 写法1: String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 @@ -164,7 +165,7 @@ public class ReadTest { } /** - * 数据转换等异常处理 + * 读取公式和单元格类型 * *

* 1. 创建excel对应的实体对象 参照{@link DemoData} @@ -174,10 +175,27 @@ public class ReadTest { * 3. 直接读即可 */ @Test + public void cellDataRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead(); + } + + /** + * 数据转换等异常处理 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData} + *

+ * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} + *

+ * 3. 直接读即可 + */ + @Test public void exceptionRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); + EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead(); } /** diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java index 8fff02ca..cc525629 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java @@ -36,7 +36,7 @@ public class WebTest { */ @GetMapping("download") public void download(HttpServletResponse response) throws IOException { - // 这里注意 有同学反应下载的文件名不对。这个时候 请别使用swagger 他会有影响 + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java index 2e8931cc..fb39de15 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java +++ b/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.FileInputStream; import java.util.List; +import java.util.Map; import org.junit.Ignore; import org.junit.Test; @@ -35,4 +36,14 @@ public class LockTest { } } + @Test + public void test2() throws Exception { + List 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)); + } + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java index 5c1620e2..3a70d0d4 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -18,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.metadata.CellData; +import com.alibaba.fastjson.JSON; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; @@ -30,21 +31,49 @@ import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; public class PoiWriteTest { 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 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(); SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); SXSSFRow row = sheet.createRow(0); SXSSFCell cell1 = row.createCell(0); - cell1.setCellValue(1); + cell1.setCellValue(Long.toString(999999999999999L)); SXSSFCell cell2 = row.createCell(1); - cell2.setCellValue(1); - SXSSFCell cell3 = row.createCell(2); - cell3.setCellFormula("=A1+B1"); + cell2.setCellValue(Long.toString(1000000000000001L)); 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("^.*?\\$\\{[^}]+}.*?$"); @Test diff --git a/src/test/resources/demo/cellDataDemo.xlsx b/src/test/resources/demo/cellDataDemo.xlsx new file mode 100644 index 00000000..947229c7 Binary files /dev/null and b/src/test/resources/demo/cellDataDemo.xlsx differ diff --git a/update.md b/update.md index b4dde639..04eec043 100644 --- a/update.md +++ b/update.md @@ -2,6 +2,10 @@ * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 +* 新增写支持 禁用头样式 autoHeadStyle +* 用map读取数据 空的单元格也会有个 null的数据 +* 转换报错 能获取到对应的行号和列号 +* 优化读取全部sheet方案 # 2.0.5 * 优化07版超大文件读取方案