From 33929c717017658e3fc65741f7c4eb5d6ecb00a6 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 16 Sep 2019 18:29:31 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8string?= =?UTF-8?q?=E5=8E=BB=E6=8E=A5=E6=94=B6number=E6=95=B0=E6=8D=AE=E4=BC=9A?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=A7=91=E5=AD=A6=E8=AE=A1=E6=95=B0=E6=B3=95?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/alibaba/excel/EasyExcelFactory.java | 2 +- .../v03/handlers/FormulaRecordHandler.java | 4 +- .../v03/handlers/NumberRecordHandler.java | 4 +- .../v07/handlers/DefaultCellHandler.java | 5 +- .../bigdecimal/BigDecimalNumberConverter.java | 4 +- .../BooleanNumberConverter.java | 12 +-- .../byteconverter/ByteNumberConverter.java | 6 +- .../converters/date/DateNumberConverter.java | 16 ++- .../DoubleNumberConverter.java | 6 +- .../floatconverter/FloatNumberConverter.java | 6 +- .../integer/IntegerNumberConverter.java | 6 +- .../longconverter/LongNumberConverter.java | 6 +- .../shortconverter/ShortNumberConverter.java | 6 +- .../string/StringNumberConverter.java | 14 +-- .../com/alibaba/excel/metadata/CellData.java | 57 +++++++--- .../listener/ModelBuildEventListener.java | 6 +- .../alibaba/excel/util/ConverterUtils.java | 40 ++++++- .../alibaba/excel/write/ExcelBuilderImpl.java | 39 +++++-- .../LongestMatchColumnWidthStyleStrategy.java | 2 +- .../easyexcel/test/temp/poi/PoiWriteTest.java | 101 ++++++++++++++++++ .../easyexcel/test/temp/poi/TestCell.java | 18 ++++ .../easyexcel/test/temp/simple/HgTest.java | 2 +- 22 files changed, 297 insertions(+), 65 deletions(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java diff --git a/src/main/java/com/alibaba/excel/EasyExcelFactory.java b/src/main/java/com/alibaba/excel/EasyExcelFactory.java index 39fe031..a492497 100644 --- a/src/main/java/com/alibaba/excel/EasyExcelFactory.java +++ b/src/main/java/com/alibaba/excel/EasyExcelFactory.java @@ -73,7 +73,7 @@ public class EasyExcelFactory { * read sheet. * @param listener * Callback method after each row is parsed. - * @deprecated please use 'EasyExcel.read(in,head,listener).sheet(sheetNo).doRead().finish();' + * @deprecated please use 'EasyExcel.read(in,head,listener).sheet(sheetNo).doRead();' */ @Deprecated public static void readBySax(InputStream in, Sheet sheet, AnalysisEventListener listener) { diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java index f8899c0..0ff8f99 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java @@ -1,5 +1,7 @@ package com.alibaba.excel.analysis.v03.handlers; +import java.math.BigDecimal; + import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.model.HSSFFormulaParser; import org.apache.poi.hssf.record.FormulaRecord; @@ -66,7 +68,7 @@ public class FormulaRecordHandler extends AbstractXlsRecordHandler { tempCellData.setFormulaValue(formulaValue); break; case NUMERIC: - this.cellData = new CellData(frec.getValue()); + this.cellData = new CellData(BigDecimal.valueOf(frec.getValue())); this.cellData.setFormula(Boolean.TRUE); this.cellData.setFormulaValue(formulaValue); break; diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java index 23aff5a..75c3128 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java @@ -1,5 +1,7 @@ package com.alibaba.excel.analysis.v03.handlers; +import java.math.BigDecimal; + import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; import org.apache.poi.hssf.record.NumberRecord; import org.apache.poi.hssf.record.Record; @@ -29,7 +31,7 @@ public class NumberRecordHandler extends AbstractXlsRecordHandler { NumberRecord numrec = (NumberRecord)record; this.row = numrec.getRow(); this.column = numrec.getColumn(); - this.cellData = new CellData(numrec.getValue()); + this.cellData = new CellData(BigDecimal.valueOf(numrec.getValue())); this.cellData.setDataFormat(formatListener.getFormatIndex(numrec)); this.cellData.setDataFormatString(formatListener.getFormatString(numrec)); } diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java index 37d12ad..5a52c99 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java @@ -7,6 +7,7 @@ import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_TAG; import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TAG; import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TYPE_TAG; +import java.math.BigDecimal; import java.util.Map; import java.util.TreeMap; @@ -162,8 +163,8 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder case NUMBER: case EMPTY: currentCellData.setType(CellDataTypeEnum.NUMBER); - if (currentCellData.getDoubleValue() == null) { - currentCellData.setDoubleValue(Double.valueOf(currentCellValue)); + if (currentCellData.getNumberValue() == null) { + currentCellData.setNumberValue(new BigDecimal(currentCellValue)); } break; default: diff --git a/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java b/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java index f7f1405..bf9b735 100644 --- a/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java @@ -28,12 +28,12 @@ public class BigDecimalNumberConverter implements Converter { @Override public BigDecimal convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return BigDecimal.valueOf(cellData.getDoubleValue()); + return cellData.getNumberValue(); } @Override public CellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(value); } } diff --git a/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java b/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java index 44cbdb9..06ac0c7 100644 --- a/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.booleanconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -12,10 +14,6 @@ import com.alibaba.excel.metadata.property.ExcelContentProperty; * @author Jiaju Zhuang */ public class BooleanNumberConverter implements Converter { - - private static final Double ONE = 1.0; - private static final Double ZERO = 0.0; - @Override public Class supportJavaTypeKey() { return Boolean.class; @@ -29,7 +27,7 @@ public class BooleanNumberConverter implements Converter { @Override public Boolean convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - if (ONE.equals(cellData.getDoubleValue())) { + if (BigDecimal.ONE.equals(cellData.getNumberValue())) { return Boolean.TRUE; } return Boolean.FALSE; @@ -39,9 +37,9 @@ public class BooleanNumberConverter implements Converter { public CellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (value) { - return new CellData(ONE); + return new CellData(BigDecimal.ONE); } - return new CellData(ZERO); + return new CellData(BigDecimal.ZERO); } } diff --git a/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java b/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java index c3ecf47..8c16a77 100644 --- a/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.byteconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class ByteNumberConverter implements Converter { @Override public Byte convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue().byteValue(); + return cellData.getNumberValue().byteValue(); } @Override public CellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java b/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java index 810188a..fcc76bb 100644 --- a/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java @@ -1,8 +1,9 @@ package com.alibaba.excel.converters.date; +import java.math.BigDecimal; import java.util.Date; -import org.apache.poi.hssf.usermodel.HSSFDateUtil; +import org.apache.poi.ss.usermodel.DateUtil; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; @@ -31,9 +32,10 @@ public class DateNumberConverter implements Converter { public Date convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return HSSFDateUtil.getJavaDate(cellData.getDoubleValue(), globalConfiguration.getUse1904windowing(), null); + return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), + globalConfiguration.getUse1904windowing(), null); } else { - return HSSFDateUtil.getJavaDate(cellData.getDoubleValue(), + return DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null); } } @@ -41,6 +43,12 @@ public class DateNumberConverter implements Converter { @Override public CellData convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData((double)(value.getTime())); + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new CellData( + BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + } else { + return new CellData(BigDecimal.valueOf( + DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + } } } diff --git a/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java b/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java index f272c82..15fbdae 100644 --- a/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.doubleconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class DoubleNumberConverter implements Converter { @Override public Double convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue(); + return cellData.getNumberValue().doubleValue(); } @Override public CellData convertToExcelData(Double value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java b/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java index 8913a57..a1b6471 100644 --- a/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.floatconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class FloatNumberConverter implements Converter { @Override public Float convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue().floatValue(); + return cellData.getNumberValue().floatValue(); } @Override public CellData convertToExcelData(Float value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java b/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java index 4dff32d..3b0deac 100644 --- a/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.integer; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class IntegerNumberConverter implements Converter { @Override public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue().intValue(); + return cellData.getNumberValue().intValue(); } @Override public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java b/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java index bceedd5..8058d62 100644 --- a/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.longconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class LongNumberConverter implements Converter { @Override public Long convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue().longValue(); + return cellData.getNumberValue().longValue(); } @Override public CellData convertToExcelData(Long value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java index 91f6579..7d1d7da 100644 --- a/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.shortconverter; +import java.math.BigDecimal; + import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.metadata.CellData; @@ -26,13 +28,13 @@ public class ShortNumberConverter implements Converter { @Override public Short convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return cellData.getDoubleValue().shortValue(); + return cellData.getNumberValue().shortValue(); } @Override public CellData convertToExcelData(Short value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(value.doubleValue()); + return new CellData(BigDecimal.valueOf(value)); } } diff --git a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java index 62b2523..89cbe1d 100644 --- a/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java +++ b/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java @@ -1,5 +1,7 @@ package com.alibaba.excel.converters.string; +import java.math.BigDecimal; + import org.apache.poi.hssf.usermodel.HSSFDateUtil; import org.apache.poi.ss.usermodel.DateUtil; @@ -34,30 +36,30 @@ public class StringNumberConverter implements Converter { // If there are "DateTimeFormat", read as date if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { return DateUtils.format( - HSSFDateUtil.getJavaDate(cellData.getDoubleValue(), + DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), contentProperty.getDateTimeFormatProperty().getUse1904windowing(), null), contentProperty.getDateTimeFormatProperty().getFormat()); } // If there are "NumberFormat", read as number if (contentProperty != null && contentProperty.getNumberFormatProperty() != null) { - return NumberUtils.format(cellData.getDoubleValue(), contentProperty); + return NumberUtils.format(cellData.getNumberValue(), contentProperty); } // Excel defines formatting if (cellData.getDataFormat() != null) { if (DateUtil.isADateFormat(cellData.getDataFormat(), cellData.getDataFormatString())) { - return DateUtils.format(HSSFDateUtil.getJavaDate(cellData.getDoubleValue(), + return DateUtils.format(DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(), globalConfiguration.getUse1904windowing(), null)); } else { - return NumberUtils.format(cellData.getDoubleValue(), contentProperty); + return NumberUtils.format(cellData.getNumberValue(), contentProperty); } } // Default conversion number - return NumberUtils.format(cellData.getDoubleValue(), contentProperty); + return NumberUtils.format(cellData.getNumberValue(), contentProperty); } @Override public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return new CellData(Double.valueOf(value)); + return new CellData(new BigDecimal(value)); } } diff --git a/src/main/java/com/alibaba/excel/metadata/CellData.java b/src/main/java/com/alibaba/excel/metadata/CellData.java index 0a1ffeb..a3a40c6 100644 --- a/src/main/java/com/alibaba/excel/metadata/CellData.java +++ b/src/main/java/com/alibaba/excel/metadata/CellData.java @@ -1,19 +1,23 @@ package com.alibaba.excel.metadata; +import java.math.BigDecimal; + import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.util.StringUtils; /** - * Excel internal cell data + * Excel internal cell data. + * + *

* * @author Jiaju Zhuang */ -public class CellData { +public class CellData { private CellDataTypeEnum type; /** * {@link CellDataTypeEnum#NUMBER} */ - private Double doubleValue; + private BigDecimal numberValue; /** * {@link CellDataTypeEnum#STRING} and{@link CellDataTypeEnum#ERROR} */ @@ -33,10 +37,14 @@ public class CellData { * The string of number formatting.Currently only supported when reading */ private String dataFormatString; + /** + * The resulting converted data. + */ + private T data; - public CellData(CellData other) { + public CellData(CellData other) { this.type = other.type; - this.doubleValue = other.doubleValue; + this.numberValue = other.numberValue; this.stringValue = other.stringValue; this.booleanValue = other.booleanValue; this.formula = other.formula; @@ -44,6 +52,19 @@ public class CellData { this.imageValue = other.imageValue; this.dataFormat = other.dataFormat; this.dataFormatString = other.dataFormatString; + this.data = other.data; + } + + public CellData() {} + + public CellData(T data) { + this.data = data; + } + + public CellData(T data, String formulaValue) { + this.data = data; + this.formula = Boolean.TRUE; + this.formulaValue = formulaValue; } public CellData(String stringValue) { @@ -62,12 +83,12 @@ public class CellData { this.formula = Boolean.FALSE; } - public CellData(Double doubleValue) { - if (doubleValue == null) { + public CellData(BigDecimal numberValue) { + if (numberValue == null) { throw new IllegalArgumentException("DoubleValue can not be null"); } this.type = CellDataTypeEnum.NUMBER; - this.doubleValue = doubleValue; + this.numberValue = numberValue; this.formula = Boolean.FALSE; } @@ -105,12 +126,12 @@ public class CellData { this.type = type; } - public Double getDoubleValue() { - return doubleValue; + public BigDecimal getNumberValue() { + return numberValue; } - public void setDoubleValue(Double doubleValue) { - this.doubleValue = doubleValue; + public void setNumberValue(BigDecimal numberValue) { + this.numberValue = numberValue; } public String getStringValue() { @@ -169,6 +190,14 @@ public class CellData { this.dataFormatString = dataFormatString; } + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + /** * Ensure that the object does not appear null */ @@ -181,7 +210,7 @@ public class CellData { } return; case NUMBER: - if (doubleValue == null) { + if (numberValue == null) { type = CellDataTypeEnum.EMPTY; } return; @@ -198,7 +227,7 @@ public class CellData { public String toString() { switch (type) { case NUMBER: - return doubleValue.toString(); + return numberValue.toString(); case BOOLEAN: return booleanValue.toString(); case STRING: 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 7896334..6bea593 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -49,7 +49,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener map.put(entry.getKey(), null); continue; } - map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, String.class, null, + map.put(entry.getKey(), (String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); } return map; @@ -62,7 +62,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener list.add(null); continue; } - list.add((String)ConverterUtils.convertToJavaObject(cellData, String.class, null, + list.add((String)ConverterUtils.convertToJavaObject(cellData, null, null, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration())); } return list; @@ -91,7 +91,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener continue; } ExcelContentProperty excelContentProperty = contentPropertyMap.get(index); - Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField().getType(), + Object value = ConverterUtils.convertToJavaObject(cellData, excelContentProperty.getField(), excelContentProperty, currentReadHolder.converterMap(), currentReadHolder.globalConfiguration()); 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 7445b4c..a06ab38 100644 --- a/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -1,5 +1,8 @@ package com.alibaba.excel.util; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; @@ -56,17 +59,48 @@ public class ConverterUtils { * Convert it into a Java object * * @param cellData - * @param clazz + * @param field * @param contentProperty * @param converterMap * @param globalConfiguration * @return */ - public static Object convertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, + public static Object convertToJavaObject(CellData cellData, Field field, ExcelContentProperty contentProperty, Map converterMap, GlobalConfiguration globalConfiguration) { + Class clazz; + if (field == null) { + clazz = String.class; + } else { + clazz = field.getType(); + } if (clazz == CellData.class) { - return new CellData(cellData); + Type type = field.getGenericType(); + Class classGeneric; + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + classGeneric = (Class)((ParameterizedType)parameterizedType.getActualTypeArguments()[0]).getRawType(); + } else { + classGeneric = String.class; + } + CellData cellDataReturn = new CellData(cellData); + cellDataReturn.setData( + doConvertToJavaObject(cellData, classGeneric, contentProperty, converterMap, globalConfiguration)); + return cellDataReturn; } + return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, globalConfiguration); + } + + /** + * + * @param cellData + * @param clazz + * @param contentProperty + * @param converterMap + * @param globalConfiguration + * @return + */ + private static Object doConvertToJavaObject(CellData cellData, Class clazz, ExcelContentProperty contentProperty, + Map converterMap, GlobalConfiguration globalConfiguration) { Converter converter = null; if (contentProperty != null) { converter = contentProperty.getConverter(); diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 3c25726..e0e8503 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -21,6 +21,7 @@ import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContextImpl; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.exception.ExcelDataConvertException; import com.alibaba.excel.exception.ExcelGenerateException; @@ -291,16 +292,12 @@ public class ExcelBuilderImpl implements ExcelBuilder { private CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, ExcelContentProperty excelContentProperty) { if (value == null) { - return null; + return new CellData(); } if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { value = ((String)value).trim(); } CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); - if (cellData == null || cellData.getType() == null) { - throw new ExcelDataConvertException( - "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); - } if (cellData.getFormula() != null && cellData.getFormula()) { cell.setCellFormula(cellData.getFormulaValue()); } @@ -312,11 +309,13 @@ public class ExcelBuilderImpl implements ExcelBuilder { cell.setCellValue(cellData.getBooleanValue()); return cellData; case NUMBER: - cell.setCellValue(cellData.getDoubleValue()); + cell.setCellValue(cellData.getNumberValue().doubleValue()); return cellData; case IMAGE: setImageValue(cellData, cell); return cellData; + case EMPTY: + return cellData; default: throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + "at row:" + cell.getRow().getRowNum()); @@ -346,9 +345,31 @@ public class ExcelBuilderImpl implements ExcelBuilder { private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, ExcelContentProperty excelContentProperty) { + // This means that the user has defined the data. if (value instanceof CellData) { - return (CellData)value; + CellData cellDataValue = (CellData)value; + if (cellDataValue.getType() != null) { + return cellDataValue; + } else { + if (cellDataValue.getData() == null) { + cellDataValue.setType(CellDataTypeEnum.EMPTY); + return cellDataValue; + } + } + CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, + cellDataValue.getData(), excelContentProperty); + // The formula information is subject to user input + if (cellDataValue.getFormula() != null) { + cellDataReturn.setFormula(cellDataValue.getFormula()); + cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); + } + return cellDataReturn; } + return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); + } + + private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { Converter converter = null; if (excelContentProperty != null) { converter = excelContentProperty.getConverter(); @@ -368,6 +389,10 @@ public class ExcelBuilderImpl implements ExcelBuilder { throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), e); } + if (cellData == null || cellData.getType() == null) { + throw new ExcelDataConvertException( + "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); + } return cellData; } } diff --git a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java index 5c6ec8c..320573b 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -58,7 +58,7 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty case BOOLEAN: return cellData.getBooleanValue().toString().getBytes().length; case NUMBER: - return cellData.getDoubleValue().toString().getBytes().length; + return cellData.getNumberValue().toString().getBytes().length; default: return -1; } 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 new file mode 100644 index 0000000..02e38c0 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -0,0 +1,101 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import org.apache.poi.ss.formula.functions.T; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.metadata.CellData; + +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ +@Ignore +public class PoiWriteTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiWriteTest.class); + + @Test + public void write() throws IOException { + FileOutputStream fileOutputStream = new FileOutputStream("D://test//tt12.xlsx"); + SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); + SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); + SXSSFRow row = sheet.createRow(0); + SXSSFCell cell1 = row.createCell(0); + cell1.setCellValue(1); + SXSSFCell cell2 = row.createCell(1); + cell2.setCellValue(1); + SXSSFCell cell3 = row.createCell(2); + cell3.setCellFormula("=A1+B1"); + sxxsFWorkbook.write(fileOutputStream); + } + + @Test + public void test() throws Exception { + Class clazz = TestCell.class; + + Field field = clazz.getDeclaredField("c2"); + // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 + + Type gType = field.getGenericType(); + // 获得field的泛型类型 + + // 如果gType是ParameterizedType对象(参数化) + if (gType instanceof ParameterizedType) { + + ParameterizedType pType = (ParameterizedType)gType; + // 就把它转换成ParameterizedType对象 + + Type[] tArgs = pType.getActualTypeArguments(); + // 获得泛型类型的泛型参数(实际类型参数) + ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; + Class ttt = c.getRawType(); + System.out.println(ttt); + } else { + System.out.println("出错!!!"); + } + + } + + @Test + public void test2() throws Exception { + Class clazz = TestCell.class; + + Field field = clazz.getDeclaredField("c2"); + // 通过getDeclaredField可以获得成员变量,但是对于Map来说,仅仅可以知道它是个Map,无法知道键值对各自的数据类型 + + Type gType = field.getGenericType(); + // 获得field的泛型类型 + + // 如果gType是ParameterizedType对象(参数化) + if (gType instanceof ParameterizedType) { + + ParameterizedType pType = (ParameterizedType)gType; + // 就把它转换成ParameterizedType对象 + + Type[] tArgs = pType.getActualTypeArguments(); + // 获得泛型类型的泛型参数(实际类型参数) + ParameterizedTypeImpl c = (ParameterizedTypeImpl)pType.getActualTypeArguments()[0]; + Class ttt = c.getRawType(); + System.out.println(ttt); + } else { + System.out.println("出错!!!"); + } + + } + +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java new file mode 100644 index 0000000..3c4e0ab --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.util.List; + +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * TODO + * + * @author 罗成 + **/ +@Data +public class TestCell { + private CellData c1; + private CellData> c2; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index 4ece35e..e0032fd 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -24,7 +24,7 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\商户不匹配工单信息收集表格.xlsx")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\test.xlsx")).headRowNumber(0).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } From 3ea045c502a639d24bbc5874ad8761578d911874 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Thu, 19 Sep 2019 20:53:37 +0800 Subject: [PATCH 02/17] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E3=80=81=E5=AF=BC=E5=87=BA=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=85=AC=E5=BC=8F=20=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=8D=95=E5=85=83=E6=A0=BC=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E3=80=81=E5=86=99=E5=85=A5=E6=8C=87=E5=AE=9A=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=A0=BC=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../alibaba/excel/util/ConverterUtils.java | 2 +- .../alibaba/excel/write/ExcelBuilderImpl.java | 2 + .../test/core/celldata/CellDataData.java | 21 ++++++ .../core/celldata/CellDataDataListener.java | 37 ++++++++++ .../test/core/celldata/CellDataDataTest.java | 71 +++++++++++++++++++ update.md | 15 ++++ 7 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java diff --git a/pom.xml b/pom.xml index 754c945..68fe674 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.0.0 + 2.1.0-beta1 jar easyexcel diff --git a/src/main/java/com/alibaba/excel/util/ConverterUtils.java b/src/main/java/com/alibaba/excel/util/ConverterUtils.java index a06ab38..1a1e70a 100644 --- a/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -78,7 +78,7 @@ public class ConverterUtils { Class classGeneric; if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; - classGeneric = (Class)((ParameterizedType)parameterizedType.getActualTypeArguments()[0]).getRawType(); + classGeneric = (Class)parameterizedType.getActualTypeArguments()[0]; } else { classGeneric = String.class; } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index e0e8503..5a21d44 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -29,8 +29,10 @@ import com.alibaba.excel.metadata.BaseRowModel; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.handler.RowWriteHandler; diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java new file mode 100644 index 0000000..a4e624f --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.metadata.CellData; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class CellDataData { + @DateTimeFormat("yyyy年MM月dd日") + private CellData date; + private CellData integer1; + private Integer integer2; + private CellData formulaValue; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java new file mode 100644 index 0000000..3108f97 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java @@ -0,0 +1,37 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSON; + +/** + * @author Jiaju Zhuang + */ +public class CellDataDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(CellDataData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assert.assertEquals(list.size(), 1); + CellDataData cellDataData = list.get(0); + + Assert.assertEquals(cellDataData.getDate().getStringValue(), "2020年01月01日"); + Assert.assertEquals((long)cellDataData.getInteger1().getData(), 2L); + Assert.assertEquals((long)cellDataData.getInteger2(), 2L); + Assert.assertEquals(cellDataData.getFormulaValue().getFormulaValue(), "B2+C2"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java new file mode 100644 index 0000000..4615cdb --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java @@ -0,0 +1,71 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.easyexcel.test.core.simple.SimpleDataSheetNameListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.util.DateUtils; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class CellDataDataTest { + + private static File file07; + private static File file03; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("cellData07.xlsx"); + file03 = TestFileUtil.createNewFile("cellData03.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(file, CellDataData.class).sheet().doWrite(data()); + EasyExcel.read(file, CellDataData.class, new CellDataDataListener()).sheet().doRead(); + } + + private List data() throws Exception { + List list = new ArrayList(); + CellDataData cellDataData = new CellDataData(); + cellDataData.setDate(new CellData(DateUtils.parseDate("2020-01-01 01:01:01"))); + CellData integer1 = new CellData(); + integer1.setType(CellDataTypeEnum.NUMBER); + integer1.setNumberValue(BigDecimal.valueOf(2L)); + cellDataData.setInteger1(integer1); + cellDataData.setInteger2(2); + CellData formulaValue = new CellData(); + formulaValue.setFormula(Boolean.TRUE); + formulaValue.setFormulaValue("B2+C2"); + cellDataData.setFormulaValue(formulaValue); + list.add(cellDataData); + return list; + } +} diff --git a/update.md b/update.md index 0efb5ef..47cb445 100644 --- a/update.md +++ b/update.md @@ -1,3 +1,8 @@ +# 2.1.0-beta1 +* 降级poi为3.1.7 兼容jdk6 +* 新增支持导入、导出支持公式 +* 新增支持读取单元格类型、写入指定单元格类型 + # 2.0.0 * 修复当cell为空可能会抛出空指针的bug * 修复电话等长数字可能出现科学计数法的问题 [Issue #583](https://github.com/alibaba/easyexcel/issues/583) @@ -33,23 +38,33 @@ * sheetNo 改成0开始 * 读支持指定列名 * 升级poi 到4.0.1 + # 1.2.4 修复read()方法存在的bug + # 1.2.1 修复POI在大并发情况下创建临时目录失败的bug + # 1.0.9 修复excel超过16列被覆盖的问题,修复数据只有一行时候无法透传的bug。 + # 1.0.8 如果整行excel数据全部为空,则不解析返回。完善多sheet的解析。 + # 1.0.6 增加@ExcelColumnNum,修复字符串前后空白,增加过滤功能。 + # 1.0.5 优化类型转换的性能。 + # 1.0.4 修复日期类型转换时候数字问题。基础模型支持字段类型int,long,double,boolean,date,string + # 1.0.3 修复无@ExcelProperty标注的多余字段时候报错。 + # 1.0.2 修复拿到一行数据后,存到list中,但最后处理时候变为空的bug。 + # 1.0.1 完善测试用例,防止歧义,模型字段映射不上时候有抛异常,改为提醒。 \ No newline at end of file From bd869edafa3d94eabbd6bfdb9834342d2e8a3248 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Fri, 20 Sep 2019 22:50:30 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../alibaba/excel/write/ExcelBuilderImpl.java | 77 ++++++++++++++++++- .../write/metadata/fill/AnalysisCell.java | 47 +++++++++++ update.md | 1 + 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 5a21d44..a7cc6b5 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -3,16 +3,19 @@ package com.alibaba.excel.write; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; @@ -29,10 +32,8 @@ import com.alibaba.excel.metadata.BaseRowModel; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.property.ExcelContentProperty; -import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; -import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.handler.RowWriteHandler; @@ -40,6 +41,7 @@ import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.AnalysisCell; import com.alibaba.excel.write.metadata.holder.WriteHolder; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; @@ -50,6 +52,10 @@ import net.sf.cglib.beans.BeanMap; */ public class ExcelBuilderImpl implements ExcelBuilder { + private static final String FILL_PREFIX = "${"; + private static final String FILL_SUFFIX = "}"; + private static final Pattern FILL_PATTERN = Pattern.compile("\\$\\{[^}]+}"); + private WriteContext context; public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { @@ -103,6 +109,73 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } + public void fill(Object data, WriteSheet writeSheet) { + try { + if (context.writeWorkbookHolder().getTemplateFile() == null + && context.writeWorkbookHolder().getTemplateInputStream() == null) { + throw new ExcelGenerateException("Calling the 'fill' method must use a template."); + } + context.currentSheet(writeSheet); + doFill(data); + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelGenerateException(e); + } + } + + private void doFill(Object data) { + BeanMap beanMap = BeanMap.create(data); + WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); + + Sheet sheet = writeSheetHolder.getSheet(); + Map templateLastRowMap = context.writeWorkbookHolder().getTemplateLastRowMap(); + if (!templateLastRowMap.containsKey(writeSheetHolder.getSheetNo())) { + throw new ExcelGenerateException( + "The corresponding table cannot be found,sheetNo:" + writeSheetHolder.getSheetNo()); + } + Map analysisCellMap = new HashMap(16); + + for (int i = 0; i < templateLastRowMap.get(writeSheetHolder.getSheetNo()); i++) { + Row row = sheet.getRow(i); + for (int j = 0; j < row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + String value = cell.getStringCellValue(); + if (FILL_PATTERN.matcher(value).matches()) { + AnalysisCell analysisCell = new AnalysisCell(); + analysisCell.setRowIndex(i); + analysisCell.setColumnIndex(j); + List variableList = new ArrayList(); + analysisCell.setVariableList(variableList); + boolean matches = true; + while (matches) { + + matches = FILL_PATTERN.matcher(value).matches(); + } + + } + } + + } + + if (CollectionUtils.isEmpty(data)) { + return; + } + WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { + newRowIndex += context.currentWriteHolder().relativeHeadRowIndex(); + } + // BeanMap is out of order,so use fieldList + List fieldList = new ArrayList(); + for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { + int n = relativeRowIndex + newRowIndex; + addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); + } + } + @Override public void finish() { if (context != null) { diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java new file mode 100644 index 0000000..4613891 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.List; + +/** + * Read the cells of the template while populating the data. + * + * @author Jiaju Zhuang + **/ +public class AnalysisCell { + private int columnIndex; + private int rowIndex; + private List variableList; + private String prepareData; + + public int getColumnIndex() { + return columnIndex; + } + + public void setColumnIndex(int columnIndex) { + this.columnIndex = columnIndex; + } + + public int getRowIndex() { + return rowIndex; + } + + public void setRowIndex(int rowIndex) { + this.rowIndex = rowIndex; + } + + public List getVariableList() { + return variableList; + } + + public void setVariableList(List variableList) { + this.variableList = variableList; + } + + public String getPrepareData() { + return prepareData; + } + + public void setPrepareData(String prepareData) { + this.prepareData = prepareData; + } +} diff --git a/update.md b/update.md index 47cb445..ed84d1c 100644 --- a/update.md +++ b/update.md @@ -2,6 +2,7 @@ * 降级poi为3.1.7 兼容jdk6 * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 +* 支持通过模板填充数据 # 2.0.0 * 修复当cell为空可能会抛出空指针的bug From d2c8d205dea5a36196a3656f41c47c32c855055e Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 23 Sep 2019 19:56:22 +0800 Subject: [PATCH 04/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alibaba/excel/ExcelWriter.java | 12 +++ .../com/alibaba/excel/util/WorkBookUtil.java | 1 + .../com/alibaba/excel/write/ExcelBuilder.java | 8 ++ .../alibaba/excel/write/ExcelBuilderImpl.java | 62 +++++++----- .../builder/ExcelWriterSheetBuilder.java | 8 ++ .../metadata/holder/WriteWorkbookHolder.java | 16 +++- .../easyexcel/test/core/fill/FillData.java | 15 +++ .../test/core/fill/FillDataTest.java | 90 ++++++++++++++++++ .../easyexcel/test/temp/poi/PoiTest.java | 69 ++++++++++++++ src/test/resources/fill/simple.xlsx | Bin 0 -> 10112 bytes 10 files changed, 257 insertions(+), 24 deletions(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java create mode 100644 src/test/resources/fill/simple.xlsx diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index c99787d..d32d7ee 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -155,6 +155,18 @@ public class ExcelWriter { return this; } + /** + * Fill value to a sheet + * + * @param data + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, WriteSheet writeSheet) { + excelBuilder.fill(data, writeSheet); + return this; + } + /** * Write data to a sheet * diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index b9579b1..78cbd27 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -37,6 +37,7 @@ public class WorkBookUtil { // When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we // are using the template, so we cache it if (xssfWorkbook != null) { + writeWorkbookHolder.setXssfWorkbook(xssfWorkbook); for (int i = 0; i < xssfWorkbook.getNumberOfSheets(); i++) { writeWorkbookHolder.getTemplateLastRowMap().put(i, xssfWorkbook.getSheetAt(i).getLastRowNum()); } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java index 119854a..0570d64 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -35,6 +35,14 @@ public interface ExcelBuilder { */ void addContent(List data, WriteSheet writeSheet, WriteTable writeTable); + /** + * WorkBook fill value + * + * @param data + * @param writeSheet + */ + void fill(Object data, WriteSheet writeSheet); + /** * Creates new cell range. Indexes are zero-based. * diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index a7cc6b5..cedec44 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -2,12 +2,13 @@ package com.alibaba.excel.write; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.poi.hssf.usermodel.HSSFWorkbook; @@ -15,7 +16,6 @@ import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; @@ -109,6 +109,7 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } + @Override public void fill(Object data, WriteSheet writeSheet) { try { if (context.writeWorkbookHolder().getTemplateFile() == null @@ -127,17 +128,14 @@ public class ExcelBuilderImpl implements ExcelBuilder { } private void doFill(Object data) { - BeanMap beanMap = BeanMap.create(data); WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - Sheet sheet = writeSheetHolder.getSheet(); Map templateLastRowMap = context.writeWorkbookHolder().getTemplateLastRowMap(); - if (!templateLastRowMap.containsKey(writeSheetHolder.getSheetNo())) { + if (sheet == null) { throw new ExcelGenerateException( "The corresponding table cannot be found,sheetNo:" + writeSheetHolder.getSheetNo()); } - Map analysisCellMap = new HashMap(16); - + List analysisCellList = new ArrayList(); for (int i = 0; i < templateLastRowMap.get(writeSheetHolder.getSheetNo()); i++) { Row row = sheet.getRow(i); for (int j = 0; j < row.getLastCellNum(); j++) { @@ -150,29 +148,49 @@ public class ExcelBuilderImpl implements ExcelBuilder { List variableList = new ArrayList(); analysisCell.setVariableList(variableList); boolean matches = true; + int index = 0; while (matches) { - + Matcher matcher = FILL_PATTERN.matcher(value); + String variable = value.substring(matcher.start(), matcher.end()); + variableList.add(variable); + value = matcher.replaceFirst("{" + index++ + "}"); matches = FILL_PATTERN.matcher(value).matches(); + analysisCellList.add(analysisCell); } - } } - } - if (CollectionUtils.isEmpty(data)) { - return; - } - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); - if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { - newRowIndex += context.currentWriteHolder().relativeHeadRowIndex(); + if (data instanceof Collection) { + + } else if (data instanceof Map) { + + } else { + } - // BeanMap is out of order,so use fieldList - List fieldList = new ArrayList(); - for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { - int n = relativeRowIndex + newRowIndex; - addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); + BeanMap beanMap = BeanMap.create(data); + + for (AnalysisCell analysisCell : analysisCellList) { + Cell cell = sheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); + if (analysisCell.getVariableList().size() == 1) { + Object value = beanMap.get(analysisCell.getVariableList().get(0)); + if (value == null) { + continue; + } + converterAndSet(writeSheetHolder, value.getClass(), cell, value, null); + } else { + List fileDataStringList = new ArrayList(); + for (String variable : analysisCell.getVariableList()) { + Object value = beanMap.get(variable); + CellData cellData = convert(writeSheetHolder, String.class, cell, value, null); + String fillDataString = cellData.getStringValue(); + if (fillDataString == null) { + fillDataString = ""; + } + fileDataStringList.add(fillDataString); + } + cell.setCellValue(String.format(analysisCell.getPrepareData(), fileDataStringList)); + } } } 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 bab837d..29e8277 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -135,6 +135,14 @@ public class ExcelWriterSheetBuilder { excelWriter.finish(); } + public void doFill(Object data) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); + } + excelWriter.fill(data, build()); + excelWriter.finish(); + } + public ExcelWriterTableBuilder table() { return table(null); } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index d3c07f5..20e1f1f 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.Map; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,12 +25,15 @@ import com.alibaba.excel.write.metadata.WriteWorkbook; * @author Jiaju Zhuang */ public class WriteWorkbookHolder extends AbstractWriteHolder { - private static final Logger LOGGER = LoggerFactory.getLogger(WriteWorkbookHolder.class); /*** * poi Workbook */ private Workbook workbook; - + /** + * When reading version 07 with the template, the workbook cannot get the specific line number, so it + * needs to get the specific line number. + */ + private XSSFWorkbook xssfWorkbook; /** * current param */ @@ -130,6 +134,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.workbook = workbook; } + public XSSFWorkbook getXssfWorkbook() { + return xssfWorkbook; + } + + public void setXssfWorkbook(XSSFWorkbook xssfWorkbook) { + this.xssfWorkbook = xssfWorkbook; + } + public Map getHasBeenInitializedSheet() { return hasBeenInitializedSheet; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java new file mode 100644 index 0000000..92e18a8 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.core.fill; + +import com.alibaba.excel.annotation.format.NumberFormat; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class FillData { + private String name; + @NumberFormat("0#") + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java new file mode 100644 index 0000000..06236ca --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -0,0 +1,90 @@ +package com.alibaba.easyexcel.test.core.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.style.StyleData; +import com.alibaba.easyexcel.test.core.style.StyleDataListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class FillDataTest { + + private static File file07; + private static File file03; + private static File simpleTemplate07; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("fill07.xlsx"); + file03 = TestFileUtil.createNewFile("fill03.xls"); + simpleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "simple.xlsx"); + } + + @Test + public void t01Fill07() { + fill(file07); + } + + @Test + public void t02Fill03() { + fill(file03); + } + + private void fill(File file) { + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(file).withTemplate(simpleTemplate07).sheet().doFill(fillData); + } + + private List data() { + List list = new ArrayList(); + StyleData data = new StyleData(); + data.setString("字符串0"); + data.setString1("字符串01"); + StyleData data1 = new StyleData(); + data1.setString("字符串1"); + data1.setString1("字符串11"); + list.add(data); + list.add(data1); + return list; + } + + private List data10() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + StyleData data = new StyleData(); + data.setString("字符串0"); + data.setString1("字符串01"); + list.add(data); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index ea6dba1..5f48760 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -1,8 +1,12 @@ package com.alibaba.easyexcel.test.temp.poi; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; @@ -49,4 +53,69 @@ public class PoiTest { xssfSheet.createRow(20); LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); } + + @Test + public void lastRowNum233() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + Workbook xx=new XSSFWorkbook(file); + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + + @Test + public void lastRowNum2332222() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + Workbook xx=new XSSFWorkbook(file); + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + } + + @Test + public void lastRowNum23443() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getPhysicalNumberOfRows()); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + LOGGER.info("一共行数:{}", xssfSheet.getFirstRowNum()); + + } + + @Test + public void lastRowNumXSSF2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + } + } diff --git a/src/test/resources/fill/simple.xlsx b/src/test/resources/fill/simple.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b5d7e19a6dec1887a13c7f9cade7b82eaad3b360 GIT binary patch literal 10112 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 literal 0 HcmV?d00001 From 5331d8a6ca6e60edddbfab6867f34d2285573e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E6=88=90?= Date: Tue, 24 Sep 2019 18:35:42 +0800 Subject: [PATCH 05/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/context/WriteContextImpl.java | 28 ++++++------- .../metadata/holder/AbstractWriteHolder.java | 1 - .../metadata/holder/WriteWorkbookHolder.java | 16 +++++++- .../alibaba/easyexcel/test/temp/LockTest.java | 2 +- .../easyexcel/test/temp/poi/PoiTest.java | 39 +++++++++++++++++-- .../easyexcel/test/temp/simple/HgTest.java | 15 ++++++- 6 files changed, 79 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 6ad7fcb..0060211 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -83,7 +83,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); + ((WorkbookWriteHandler) writeHandler).beforeWorkbookCreate(); } } } @@ -95,7 +95,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeWorkbookHolder); + ((WorkbookWriteHandler) writeHandler).afterWorkbookCreate(writeWorkbookHolder); } } } @@ -149,7 +149,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); + ((SheetWriteHandler) writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); } } } @@ -161,7 +161,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); + ((SheetWriteHandler) writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); } } if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { @@ -204,7 +204,7 @@ public class WriteContextImpl implements WriteContext { // Combined head addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; - i++, relativeRowIndex++) { + i++, relativeRowIndex++) { beforeRowCreate(newRowIndex, relativeRowIndex); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); afterRowCreate(row, relativeRowIndex); @@ -219,7 +219,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, + ((RowWriteHandler) writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, relativeRowIndex, true); } } @@ -232,7 +232,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, + ((RowWriteHandler) writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, relativeRowIndex, true); } } @@ -264,7 +264,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, + ((CellWriteHandler) writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, relativeRowIndex, true); } } @@ -277,7 +277,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, + ((CellWriteHandler) writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, relativeRowIndex, true); } } @@ -354,21 +354,21 @@ public class WriteContextImpl implements WriteContext { try { Workbook workbook = writeWorkbookHolder.getWorkbook(); if (workbook instanceof SXSSFWorkbook) { - ((SXSSFWorkbook)workbook).dispose(); + ((SXSSFWorkbook) workbook).dispose(); } } catch (Throwable t) { throwCanNotCloseIo(t); } try { - if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { - writeWorkbookHolder.getOutputStream().close(); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeWorkbookHolder.getTempTemplateInputStream().close(); } } catch (Throwable t) { throwCanNotCloseIo(t); } try { - if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getTemplateInputStream() != null) { - writeWorkbookHolder.getTemplateInputStream().close(); + if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { + writeWorkbookHolder.getOutputStream().close(); } } catch (Throwable t) { throwCanNotCloseIo(t); 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 6592009..80d3e7b 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 @@ -21,7 +21,6 @@ import com.alibaba.excel.metadata.AbstractHolder; import com.alibaba.excel.metadata.Font; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.TableStyle; -import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.RowHeightProperty; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.handler.CellWriteHandler; diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 20e1f1f..0120142 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -11,8 +11,6 @@ import java.util.Map; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.exception.ExcelGenerateException; @@ -60,6 +58,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * If 'inputStream' and 'file' all not empty,file first */ private File templateFile; + /** + * Temporary template file stream. + *

+ * A temporary file stream needs to be created in order not to modify the original template file. + */ + private InputStream tempTemplateInputStream; /** * Default true */ @@ -182,6 +186,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.templateInputStream = templateInputStream; } + public InputStream getTempTemplateInputStream() { + return tempTemplateInputStream; + } + + public void setTempTemplateInputStream(InputStream tempTemplateInputStream) { + this.tempTemplateInputStream = tempTemplateInputStream; + } + public File getTemplateFile() { return templateFile; } 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 733f1a2..2e8931c 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java @@ -25,7 +25,7 @@ public class LockTest { public void test() throws Exception { List list = - EasyExcel.read(new FileInputStream("D:\\test\\t222.xlsx")).sheet().headRowNumber(0).doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\testbug嘉惠.xlsx")).sheet().headRowNumber(0).doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 5f48760..b4b1c91 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -1,6 +1,7 @@ package com.alibaba.easyexcel.test.temp.poi; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -19,6 +20,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.IoUtils; + +import ch.qos.logback.core.util.FileUtil; /** * 测试poi @@ -57,7 +62,8 @@ public class PoiTest { @Test public void lastRowNum233() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; - Workbook xx=new XSSFWorkbook(file); + Workbook xx = new XSSFWorkbook(file); + System.out.println(new File(file).exists()); SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); @@ -70,12 +76,39 @@ public class PoiTest { xssfWorkbook.close(); } + @Test + public void testread() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); +// +// Cell cell = xssfSheet.getRow(0).createCell(9); + + + String file1 = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook1 = new SXSSFWorkbook(new XSSFWorkbook(file1)); + Sheet xssfSheet1 = xssfWorkbook1.getXSSFWorkbook().getSheetAt(0); + +// Cell cell1 = xssfSheet1.getRow(0).createCell(9); + + + xssfWorkbook.close(); + xssfWorkbook1.close(); + } + + + @Test + public void testreadRead() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + FileUtils.readFileToByteArray(new File(file)); + } @Test public void lastRowNum2332222() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; - Workbook xx=new XSSFWorkbook(file); - SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); Cell cell = xssfSheet.getRow(0).createCell(9); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index 1f1db7b..7a2a791 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -2,6 +2,8 @@ package com.alibaba.easyexcel.test.temp.simple; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; import java.util.List; import org.junit.Ignore; @@ -24,7 +26,18 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\折扣2007.xls")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\嘉惠-中交建_2019-09-01_2019-09-30_1569055677522.xlsx")).headRowNumber(0).sheet().doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void hh5() throws IOException { + URL url = new URL("http://hotelcontractfil.oss-cn-beijing.aliyuncs.com/2019/%E5%98%89%E6%83%A0-%E4%B8%AD%E4%BA%A4%E5%BB%BA_2019-09-01_2019-09-30_1569055677522.xlsx?Expires=1884415681&OSSAccessKeyId=LTAIGZDkqZfPArBr&Signature=Rf0gbO8vl3l%2Brj1KdyzHHMsUhCE%3D"); + InputStream is = url.openStream(); + List list = + EasyExcel.read(is).headRowNumber(0).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } From 132bd8efa992f67f2eb1694c62d2bf0a7e64fbbb Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Thu, 26 Sep 2019 19:34:22 +0800 Subject: [PATCH 06/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abouteasyexcel.md | 2 +- quickstart.md | 11 +++++------ .../write/metadata/holder/WriteSheetHolder.java | 15 +++++++++++++++ .../easyexcel/test/demo/read/ReadTest.java | 8 ++++---- .../alibaba/easyexcel/test/temp/poi/PoiTest.java | 9 ++++----- .../easyexcel/test/temp/simple/HgTest.java | 3 ++- 6 files changed, 31 insertions(+), 17 deletions(-) diff --git a/abouteasyexcel.md b/abouteasyexcel.md index 8c06a2a..54a4e1f 100644 --- a/abouteasyexcel.md +++ b/abouteasyexcel.md @@ -50,4 +50,4 @@ Caused by: java.io.IOException: Could not create temporary directory '/home/admi ### 3、抛弃不重要的数据 -Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关系的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。 \ No newline at end of file +Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关心的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。 \ No newline at end of file diff --git a/quickstart.md b/quickstart.md index ecee760..56306d3 100644 --- a/quickstart.md +++ b/quickstart.md @@ -7,13 +7,12 @@ * 单个文件的并发写入、读取 * 读取图片 * 宏 -ClassNotFoundException与java.lang.NoClassDefFoundError的区别 * csv读取(这个后续可能会考虑) #### 常见问题 * 关于@Data,读写的对象都用到了[Lombok](https://www.projectlombok.org/),他会自动生成`get`,`set` ,如果不需要的话,自己创建对象并生成`get`,`set` 。 * 出现`NoSuchMethodException`,`ClassNotFoundException`,`NoClassDefFoundError`极大概率是jar冲突,建议`clean`项目,或者统一`poi` 的版本,理论上来说`easyexcel`兼容poi的`3.17`,`4.0.1`,`4.1.0`所有较新版本 * 如果在读的时候`Listener`里面需要使用spring的`@Autowired`,给`Listener`创建成员变量,然后在构造方法里面传进去。而别必须不让spring管理`Listener`,每次读取都要`new`一个。 -* 如果用`String`去接收数字,出现小数点等情况,这个是BUG,但是很难修复,后续版本会修复这个问题。目前请使用`@NumberFormat`直接,里面的参数就是调用了java自带的`NumberFormat.format`方法,不知道怎么入参的可以自己网上查询。 +* 如果用`String`去接收数字,出现小数点等情况,这个是BUG,但是很难修复,后续版本会修复这个问题。目前请使用`@NumberFormat`注解,里面的参数就是调用了java自带的`NumberFormat.format`方法,不知道怎么入参的可以自己网上查询。 #### 详细参数介绍 有些参数不知道怎么用,或者有些功能不知道用什么参数,参照:[详细参数介绍](/docs/API.md) #### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★Star。 @@ -291,7 +290,7 @@ public class CustomStringStringConverter implements Converter { @Test public void converterRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 // 如果就想单个字段使用请使用@ExcelProperty 指定converter @@ -321,7 +320,7 @@ public class CustomStringStringConverter implements Converter { @Test public void complexHeaderRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet() // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 .headRowNumber(1).doRead(); @@ -393,7 +392,7 @@ public class CustomStringStringConverter implements Converter { @Test public void headerRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } ``` @@ -434,7 +433,7 @@ public class CustomStringStringConverter implements Converter { @Test public void exceptionRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } ``` diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java index 7cce9f1..4d051df 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.Map; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.enums.WriteLastRowType; @@ -24,6 +26,11 @@ public class WriteSheetHolder extends AbstractWriteHolder { * poi sheet */ private Sheet sheet; + /** + * When reading version 07 with the template, the sheet cannot read template data, so need to use + * xssfSheet to get it. + */ + private XSSFSheet xssfSheet; /*** * sheetNo */ @@ -87,6 +94,14 @@ public class WriteSheetHolder extends AbstractWriteHolder { return sheetNo; } + public XSSFSheet getXssfSheet() { + return xssfSheet; + } + + public void setXssfSheet(XSSFSheet xssfSheet) { + this.xssfSheet = xssfSheet; + } + public void setSheetNo(Integer sheetNo) { this.sheetNo = sheetNo; } 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 a8e6a61..06ca4b6 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 @@ -117,7 +117,7 @@ public class ReadTest { @Test public void converterRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 // 如果就想单个字段使用请使用@ExcelProperty 指定converter @@ -140,7 +140,7 @@ public class ReadTest { @Test public void complexHeaderRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet() // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 .headRowNumber(1).doRead(); @@ -159,7 +159,7 @@ public class ReadTest { @Test public void headerRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } @@ -176,7 +176,7 @@ public class ReadTest { @Test public void exceptionRead() { String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 然后千万别忘记 finish + // 这里 需要指定读用哪个class去读,然后读取第一个sheet EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 1e872c7..7828bd0 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -1,11 +1,12 @@ package com.alibaba.easyexcel.test.temp.poi; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFRow; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; @@ -18,9 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.easyexcel.test.util.TestFileUtil; -import com.alibaba.excel.EasyExcel; - -import ch.qos.logback.core.util.FileUtil; +import com.alibaba.excel.util.FileUtils; /** * 测试poi diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index 7a2a791..821609c 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.cache.MapCache; import com.alibaba.fastjson.JSON; /** @@ -26,7 +27,7 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\嘉惠-中交建_2019-09-01_2019-09-30_1569055677522.xlsx")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\MRP生产视图(1).xlsx")).headRowNumber(0).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } From 3a2bf2b43fc4514d5a34547caa59c4ce73ac8a75 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Sun, 29 Sep 2019 19:59:15 +0800 Subject: [PATCH 07/17] =?UTF-8?q?=E4=BC=98=E5=8C=9607=E7=89=88=E8=B6=85?= =?UTF-8?q?=E5=A4=A7=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++ img/readme/large.png | Bin 0 -> 5424 bytes .../excel/context/WriteContextImpl.java | 31 ++++++++++++------ .../alibaba/excel/write/ExcelBuilderImpl.java | 7 ++-- .../metadata/holder/WriteSheetHolder.java | 1 - update.md | 1 - 6 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 img/readme/large.png diff --git a/README.md b/README.md index 93ee305..583cc4b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,11 @@ 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模式。在上层做了模型转换的封装,让使用者更加简单方便 + +## 64M内存1分钟内读取75M(46W行25列)的Excel +当然还有急速模式能更快,但是内存占用会在100M多一点 +![img](img/readme/large.png) + ## 相关文档 * [快速使用](/quickstart.md) * [关于软件](/abouteasyexcel.md) diff --git a/img/readme/large.png b/img/readme/large.png new file mode 100644 index 0000000000000000000000000000000000000000..04195a10bfef90fc51c71d2bcb55cd654710461e GIT binary patch literal 5424 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 literal 0 HcmV?d00001 diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 0060211..34b90ba 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -10,6 +10,7 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFSheet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,7 +84,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler) writeHandler).beforeWorkbookCreate(); + ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); } } } @@ -95,7 +96,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler) writeHandler).afterWorkbookCreate(writeWorkbookHolder); + ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeWorkbookHolder); } } } @@ -149,7 +150,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler) writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); + ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); } } } @@ -161,7 +162,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler) writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); + ((SheetWriteHandler)writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); } } if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { @@ -181,6 +182,16 @@ public class WriteContextImpl implements WriteContext { } private void initSheet() { + try { + if (writeWorkbookHolder.getXssfWorkbook() != null) { + writeSheetHolder + .setXssfSheet(writeWorkbookHolder.getXssfWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); + } + } catch (Exception e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Can not find XSSFSheet:{}.", writeSheetHolder.getSheetNo()); + } + } Sheet currentSheet; try { currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo()); @@ -204,7 +215,7 @@ public class WriteContextImpl implements WriteContext { // Combined head addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; - i++, relativeRowIndex++) { + i++, relativeRowIndex++) { beforeRowCreate(newRowIndex, relativeRowIndex); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); afterRowCreate(row, relativeRowIndex); @@ -219,7 +230,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler) writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, + ((RowWriteHandler)writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, relativeRowIndex, true); } } @@ -232,7 +243,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler) writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, + ((RowWriteHandler)writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, relativeRowIndex, true); } } @@ -264,7 +275,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler) writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, + ((CellWriteHandler)writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, relativeRowIndex, true); } } @@ -277,7 +288,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler) writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, + ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, relativeRowIndex, true); } } @@ -354,7 +365,7 @@ public class WriteContextImpl implements WriteContext { try { Workbook workbook = writeWorkbookHolder.getWorkbook(); if (workbook instanceof SXSSFWorkbook) { - ((SXSSFWorkbook) workbook).dispose(); + ((SXSSFWorkbook)workbook).dispose(); } } catch (Throwable t) { throwCanNotCloseIo(t); diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 2eb301a..3bbda25 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -19,6 +19,7 @@ import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContextImpl; @@ -129,14 +130,14 @@ public class ExcelBuilderImpl implements ExcelBuilder { private void doFill(Object data) { WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - Sheet sheet = writeSheetHolder.getSheet(); + XSSFSheet sheet = writeSheetHolder.getXssfSheet(); Map templateLastRowMap = context.writeWorkbookHolder().getTemplateLastRowMap(); if (sheet == null) { throw new ExcelGenerateException( "The corresponding table cannot be found,sheetNo:" + writeSheetHolder.getSheetNo()); } List analysisCellList = new ArrayList(); - for (int i = 0; i < templateLastRowMap.get(writeSheetHolder.getSheetNo()); i++) { + for (int i = 0; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); for (int j = 0; j < row.getLastCellNum(); j++) { Cell cell = row.getCell(j); @@ -151,7 +152,7 @@ public class ExcelBuilderImpl implements ExcelBuilder { int index = 0; while (matches) { Matcher matcher = FILL_PATTERN.matcher(value); - String variable = value.substring(matcher.start(), matcher.end()); + String variable = value.substring(matcher.regionStart() + 2, matcher.regionEnd() - 1); variableList.add(variable); value = matcher.replaceFirst("{" + index++ + "}"); matches = FILL_PATTERN.matcher(value).matches(); diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java index 4d051df..208fb41 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -5,7 +5,6 @@ import java.util.Map; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFSheet; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.enums.WriteLastRowType; diff --git a/update.md b/update.md index 5d5f254..3beaac9 100644 --- a/update.md +++ b/update.md @@ -1,5 +1,4 @@ # 2.1.0-beta1 -* 降级poi为3.1.7 兼容jdk6 * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 From 5ae29e36055ecde1541516265c9651ad867350b9 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Wed, 9 Oct 2019 19:46:45 +0800 Subject: [PATCH 08/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A1=AB=E5=85=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LARGEREAD.md | 6 +- img/readme/quickstart/fill/complexFill.png | Bin 0 -> 5119 bytes .../quickstart/fill/complexFillTemplate.png | Bin 0 -> 1938 bytes .../quickstart/fill/complexFillWithTable.png | Bin 0 -> 4917 bytes .../fill/complexFillWithTableTemplate.png | Bin 0 -> 1621 bytes img/readme/quickstart/fill/horizontalFill.png | Bin 0 -> 4608 bytes .../fill/horizontalFillTemplate.png | Bin 0 -> 1553 bytes img/readme/quickstart/fill/listFill.png | Bin 0 -> 1690 bytes .../quickstart/fill/listFillTemplate.png | Bin 0 -> 811 bytes img/readme/quickstart/fill/simpleFill.png | Bin 0 -> 1680 bytes .../quickstart/fill/simpleFillTemplate.png | Bin 0 -> 1841 bytes quickstart.md | 223 ++++++++- .../java/com/alibaba/excel/ExcelReader.java | 10 +- .../java/com/alibaba/excel/ExcelWriter.java | 15 +- .../alibaba/excel/analysis/ExcelAnalyser.java | 2 +- .../excel/analysis/ExcelAnalyserImpl.java | 18 +- ...elExecutor.java => ExcelReadExecutor.java} | 2 +- .../excel/analysis/v03/XlsSaxAnalyser.java | 4 +- .../excel/analysis/v07/XlsxSaxAnalyser.java | 7 +- .../excel/context/AnalysisContext.java | 6 +- .../excel/context/AnalysisContextImpl.java | 20 +- .../alibaba/excel/context/WriteContext.java | 7 +- .../excel/context/WriteContextImpl.java | 183 ++------ .../excel/enums/WriteDirectionEnum.java | 17 + ...RowType.java => WriteLastRowTypeEnum.java} | 2 +- .../WriteTemplateAnalysisCellTypeEnum.java | 17 + .../alibaba/excel/enums/WriteTypeEnum.java | 17 + .../metadata/property/ExcelHeadProperty.java | 18 +- .../com/alibaba/excel/util/StringUtils.java | 3 +- .../com/alibaba/excel/util/WorkBookUtil.java | 44 +- .../alibaba/excel/util/WriteHandlerUtils.java | 196 ++++++++ .../com/alibaba/excel/write/ExcelBuilder.java | 4 +- .../alibaba/excel/write/ExcelBuilderImpl.java | 437 +----------------- .../builder/ExcelWriterSheetBuilder.java | 7 +- .../executor/AbstractExcelWriteExecutor.java | 140 ++++++ .../write/executor/ExcelWriteAddExecutor.java | 179 +++++++ .../write/executor/ExcelWriteExecutor.java | 9 + .../executor/ExcelWriteFillExecutor.java | 377 +++++++++++++++ .../excel/write/handler/CellWriteHandler.java | 44 +- .../excel/write/handler/RowWriteHandler.java | 33 +- .../write/handler/SheetWriteHandler.java | 4 +- .../write/handler/WorkbookWriteHandler.java | 11 +- .../write/merge/AbstractMergeStrategy.java | 12 +- .../write/metadata/fill/AnalysisCell.java | 52 ++- .../excel/write/metadata/fill/FillConfig.java | 83 ++++ .../metadata/holder/WriteSheetHolder.java | 62 +-- .../metadata/holder/WriteWorkbookHolder.java | 80 ++-- .../style/AbstractCellStyleStrategy.java | 40 +- .../AbstractVerticalCellStyleStrategy.java | 4 +- .../style/HorizontalCellStyleStrategy.java | 4 +- .../AbstractColumnWidthStyleStrategy.java | 19 +- .../AbstractHeadColumnWidthStyleStrategy.java | 11 +- .../LongestMatchColumnWidthStyleStrategy.java | 20 +- .../row/AbstractRowHeightStyleStrategy.java | 15 +- .../easyexcel/test/core/fill/FillData.java | 5 +- .../test/core/fill/FillDataTest.java | 113 ++++- .../test/core/large/LargeDataTest.java | 63 ++- .../easyexcel/test/demo/fill/FillData.java | 12 + .../easyexcel/test/demo/fill/FillTest.java | 181 ++++++++ .../demo/write/CustomCellWriteHandler.java | 14 +- .../easyexcel/test/temp/large/LargeData.java | 2 + .../easyexcel/test/temp/poi/PoiTest.java | 87 +++- .../easyexcel/test/temp/poi/PoiWriteTest.java | 26 ++ .../easyexcel/test/temp/simple/HgTest.java | 2 +- src/test/resources/demo/fill/complex.xlsx | Bin 0 -> 10297 bytes .../demo/fill/complexFillWithTable.xlsx | Bin 0 -> 10290 bytes src/test/resources/demo/fill/horizontal.xlsx | Bin 0 -> 10307 bytes src/test/resources/demo/fill/list.xlsx | Bin 0 -> 10029 bytes src/test/resources/demo/fill/simple.xlsx | Bin 0 -> 10135 bytes src/test/resources/fill/complex.xls | Bin 0 -> 19456 bytes src/test/resources/fill/complex.xlsx | Bin 0 -> 10297 bytes src/test/resources/fill/horizontal.xls | Bin 0 -> 19968 bytes src/test/resources/fill/horizontal.xlsx | Bin 0 -> 10307 bytes src/test/resources/fill/simple.xls | Bin 0 -> 19456 bytes src/test/resources/fill/simple.xlsx | Bin 10112 -> 10138 bytes src/test/resources/large/fill.xlsx | Bin 0 -> 11475 bytes 76 files changed, 2163 insertions(+), 806 deletions(-) create mode 100644 img/readme/quickstart/fill/complexFill.png create mode 100644 img/readme/quickstart/fill/complexFillTemplate.png create mode 100644 img/readme/quickstart/fill/complexFillWithTable.png create mode 100644 img/readme/quickstart/fill/complexFillWithTableTemplate.png create mode 100644 img/readme/quickstart/fill/horizontalFill.png create mode 100644 img/readme/quickstart/fill/horizontalFillTemplate.png create mode 100644 img/readme/quickstart/fill/listFill.png create mode 100644 img/readme/quickstart/fill/listFillTemplate.png create mode 100644 img/readme/quickstart/fill/simpleFill.png create mode 100644 img/readme/quickstart/fill/simpleFillTemplate.png rename src/main/java/com/alibaba/excel/analysis/{ExcelExecutor.java => ExcelReadExecutor.java} (90%) create mode 100644 src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java rename src/main/java/com/alibaba/excel/enums/{WriteLastRowType.java => WriteLastRowTypeEnum.java} (91%) create mode 100644 src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java create mode 100644 src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java create mode 100644 src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java create mode 100644 src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java create mode 100644 src/test/resources/demo/fill/complex.xlsx create mode 100644 src/test/resources/demo/fill/complexFillWithTable.xlsx create mode 100644 src/test/resources/demo/fill/horizontal.xlsx create mode 100644 src/test/resources/demo/fill/list.xlsx create mode 100644 src/test/resources/demo/fill/simple.xlsx create mode 100644 src/test/resources/fill/complex.xls create mode 100644 src/test/resources/fill/complex.xlsx create mode 100644 src/test/resources/fill/horizontal.xls create mode 100644 src/test/resources/fill/horizontal.xlsx create mode 100644 src/test/resources/fill/simple.xls create mode 100644 src/test/resources/large/fill.xlsx diff --git a/docs/LARGEREAD.md b/docs/LARGEREAD.md index 512596d..8614727 100644 --- a/docs/LARGEREAD.md +++ b/docs/LARGEREAD.md @@ -9,6 +9,7 @@ ```java // 强制使用内存存储,这样大概一个20M的excel使用150M(很多临时对象,所以100M会一直GC)的内存 // 这样效率会比上面的复杂的策略高很多 + // 这里再说明下 就是加了个readCache(new MapCache()) 参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCache(new MapCache()); ``` ### 对并发要求较高,而且都是经常有超级大文件 @@ -16,7 +17,10 @@ // 第一个参数的意思是 多少M共享字符串以后 采用文件存储 单位MB 默认5M // 第二个参数 文件存储时,内存存放多少M缓存数据 默认20M // 比如 你希望用100M内存(这里说的是解析过程中的永久占用,临时对象不算)来解析excel,前面算过了 大概是 20M+90M 所以设置参数为:20 和 90 + // 这里再说明下 就是加了个readCacheSelector(new SimpleReadCacheSelector(5, 20))参数而已,其他的参照其他demo写 这里没有写全 EasyExcel.read().readCacheSelector(new SimpleReadCacheSelector(5, 20)); ``` ### 关于maxCacheActivateSize 也就是前面第二个参数的详细说明 -easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 \ No newline at end of file +easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 +### 如何判断 maxCacheActivateSize是否需要调整 +开启debug日志会输出`Already put :4000000` 最后一次输出,大概可以得出值为400W,然后看`Cache misses count:4001`得到值为4K,400W/4K=1000 这代表已经`maxCacheActivateSize` 已经非常合理了。如果小于500 问题就非常大了,500到1000 应该都还行。 diff --git a/img/readme/quickstart/fill/complexFill.png b/img/readme/quickstart/fill/complexFill.png new file mode 100644 index 0000000000000000000000000000000000000000..d37bb0f9be8c8907f226d5111ab9b7313411e240 GIT binary patch literal 5119 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 b%*ZR6B}ogslCpSkrPB`f7h literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/complexFillTemplate.png b/img/readme/quickstart/fill/complexFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..14e26b95176f0516826fb5670cf972fc8c6d9102 GIT binary patch literal 1938 zcmV;D2W|L?P)MuYxVX4?cXxN4!or<*oxJ7 z<=5BO$H&LQ!otjqw&R&>$1~ZEXR;m7WILY8c07~qcqXzl zhXIx|f^26!vK`MoB0JYGz;Z^A?W{+(HdVh=(4+#$ZT*>*d|9_j5A->CLsb*_3dTmbsotTfNo!=KEC9^4MU z?ZkEpLH2>&*i_H98U%x#8b&HI%+Q0`8F+333auSUR}EQ0(ADk_e5Lf+{H5)vhy312 z-9wge;Lp)eoRHH>X6Rl@ry!933e4GjCf{OvkN7glp=EYzq+yL87(uqP9zni^CDL$) z9~eQlvmTM1DN>N_Bq7`J-6OKIMhdc>M1*d#-!B$*y)W%8CqCITmmLIQD2O&> zTxg8Z|NmKwjL;ODZVC=`PV9r2xH*~I%FVKK2}TLqQRX~$z===a<|mLE?x?}UjM$DG^SJ|OM-~M5My(1$Vh6UP z0}w}gYjbvF_{iXZE1Mvb3co8b`w-irX;*YGLTr?sA^hL~zpaMtC}TUyja1u-U|vB= zxArP%s`%Eg9Yk_Fq+q9FpnU*e&wlM>VTq$=huF?ecDUnAQ*}FK9jH zj7iZBQ0nkaYx)@G)pxd**+H?g0ma^UDmDMob~dt8uWU$!tnD~E|2%h63t5YN#CF>M z->FwNf=XrMrXkmRzjjvHAvHVnhefI@+q=CpXl@$>qA)>khH0mha}K@a(*FN{*fL%Q z8zfd}m&`ie7c3+tkstX4BCI9Wj^v#FZxp}{4+czRETTN(H}>yIPfTvp)#(ggPN-Fz34Dy6zg-BPVnwWTlKnZ~Jt*GXYnxmjLDP5~cofM{ z8H@hB53W0HPvpWClbkCWkBmp5Oq6jzrwPf{H^%&VMb1@w)Qm@=Oq6lZXY0d#*XV$p zD_8o+cofP+8OLJrR#>I$6qe1}$4qt~;B6 z&QZ%24ABWBF`bkeIu~0upr-?+mJMkBDqq>MjeP_tv}|u2os@H0lth^F-qUmd)J1+} zHniFF*?>tp0LlWC@?vqHZR`X2ReS(#pA9%g$H&;b%ETT|0U}IVGo~tH{oB+S8#OlvSE{ z5mcw6MyH?U)W_Lu=y_wKF3Yh|zvtR=^v&rwohdz~j~AYm?+JW(-LdS$=~(vRbPW6W Y2kuj%@nx5kApigX07*qoM6N<$f}~B-(EtDd literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/complexFillWithTable.png b/img/readme/quickstart/fill/complexFillWithTable.png new file mode 100644 index 0000000000000000000000000000000000000000..c107520113287c13bdffb801826fecd563c21be1 GIT binary patch literal 4917 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 J_V47;NC1cPavWqeHeVMY1 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 literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/horizontalFillTemplate.png b/img/readme/quickstart/fill/horizontalFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..462b86ebb56544c8d5cece22dffedd80aa55be18 GIT binary patch literal 1553 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 literal 0 HcmV?d00001 diff --git a/img/readme/quickstart/fill/listFillTemplate.png b/img/readme/quickstart/fill/listFillTemplate.png new file mode 100644 index 0000000000000000000000000000000000000000..010c8c1665a547af3f340b0dc620230ab6a63a18 GIT binary patch literal 811 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 literal 0 HcmV?d00001 diff --git a/quickstart.md b/quickstart.md index db95048..728f373 100644 --- a/quickstart.md +++ b/quickstart.md @@ -47,6 +47,14 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja * [自定义拦截器(下拉,超链接等上面几点都不符合但是要对单元格进行操作的参照这个)](#customHandlerWrite) * [web中的写](#webWrite) +### 填充 +DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java) +* [最简单的填充](#simpleFill) +* [填充列表](#listFill) +* [复杂的填充](#complexFill) +* [数据量大的复杂填充](#complexFillWithTable) +* [横向的填充](#horizontalFill) + ## 读excel样例 ### 最简单的读 ##### excel示例 @@ -1021,13 +1029,19 @@ public class CustomCellWriteHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 这里可以对cell进行任何操作 LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); if (isHead && cell.getColumnIndex() == 0) { @@ -1118,6 +1132,209 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); } ``` + +## 填充excel样例 +### 最简单的填充 +##### 模板 +![img](img/readme/quickstart/fill/simpleFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/simpleFill.png) +##### 对象 +```java +@Data +public class FillData { + private String name; + private double number; +} +``` +##### 代码 +```java + /** + * 最简单的填充 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + // 方案2 根据Map填充 + fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + Map map = new HashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } +``` + +### 填充列表 +##### 模板 +![img](img/readme/quickstart/fill/listFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/listFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 填充列表 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + // 千万别忘记关闭流 + excelWriter.finish(); + } +``` + +### 复杂的填充 +##### 模板 +![img](img/readme/quickstart/fill/complexFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/complexFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 复杂的填充 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } +``` + +### 数据量大的复杂填充 +##### 模板 +![img](img/readme/quickstart/fill/complexFillWithTableTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/complexFillWithTable.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } +``` + +### 横向的填充 +##### 模板 +![img](img/readme/quickstart/fill/horizontalFillTemplate.png) +##### 最终效果 +![img](img/readme/quickstart/fill/horizontalFill.png) +##### 对象 +参照:[对象](#simpleFillObject) +##### 代码 +```java + /** + * 横向的填充 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } +``` + ## 测试数据分析 ![POI usermodel PK easyexcel(Excel 2003).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/02c4bfbbab99a649788523d04f84a42f.png) ![POI usermodel PK easyexcel(Excel 2007).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/f6a8a19ec959f0eb564e652de523fc9e.png) diff --git a/src/main/java/com/alibaba/excel/ExcelReader.java b/src/main/java/com/alibaba/excel/ExcelReader.java index 3cb6b01..808ee56 100644 --- a/src/main/java/com/alibaba/excel/ExcelReader.java +++ b/src/main/java/com/alibaba/excel/ExcelReader.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import com.alibaba.excel.analysis.ExcelAnalyser; import com.alibaba.excel.analysis.ExcelAnalyserImpl; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.MapCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; @@ -149,12 +149,12 @@ public class ExcelReader { * Parse all sheet content by default */ public void read() { - ExcelExecutor excelExecutor = excelAnalyser.excelExecutor(); - if (excelExecutor.sheetList().isEmpty()) { + ExcelReadExecutor excelReadExecutor = excelAnalyser.excelExecutor(); + if (excelReadExecutor.sheetList().isEmpty()) { LOGGER.warn("Excel doesn't have any sheets."); return; } - for (ReadSheet readSheet : excelExecutor.sheetList()) { + for (ReadSheet readSheet : excelReadExecutor.sheetList()) { read(readSheet); } } @@ -225,7 +225,7 @@ public class ExcelReader { * * @return */ - public ExcelExecutor excelExecutor() { + public ExcelReadExecutor excelExecutor() { checkFinished(); return excelAnalyser.excelExecutor(); } diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index 6ee5b6f..65a99fe 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -17,6 +17,7 @@ import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * Excel Writer This tool is used to write value out to Excel via POI. This object can perform the following two @@ -164,7 +165,19 @@ public class ExcelWriter { * @return */ public ExcelWriter fill(Object data, WriteSheet writeSheet) { - excelBuilder.fill(data, writeSheet); + return fill(data, null, writeSheet); + } + + /** + * Fill value to a sheet + * + * @param data + * @param fillConfig + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + excelBuilder.fill(data, fillConfig, writeSheet); return this; } diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java index 8d987ea..6d014bb 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java @@ -27,7 +27,7 @@ public interface ExcelAnalyser { * * @return Excel file Executor */ - ExcelExecutor excelExecutor(); + ExcelReadExecutor excelExecutor(); /** * get the analysis context. diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index cf2dff9..a924b21 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -29,7 +29,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { private AnalysisContext analysisContext; - private ExcelExecutor excelExecutor; + private ExcelReadExecutor excelReadExecutor; public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { try { @@ -48,7 +48,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); ExcelTypeEnum excelType = readWorkbookHolder.getExcelType(); if (excelType == null) { - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); return; } switch (excelType) { @@ -65,7 +65,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { try { decryptedStream = DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), null); - excelExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); return; } finally { IOUtils.closeQuietly(decryptedStream); @@ -74,10 +74,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { poifsFileSystem.close(); } } - excelExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); + excelReadExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); break; case XLSX: - excelExecutor = new XlsxSaxAnalyser(analysisContext, null); + excelReadExecutor = new XlsxSaxAnalyser(analysisContext, null); break; default: } @@ -86,9 +86,9 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { @Override public void analysis(ReadSheet readSheet) { try { - analysisContext.currentSheet(excelExecutor, readSheet); + analysisContext.currentSheet(excelReadExecutor, readSheet); try { - excelExecutor.execute(); + excelReadExecutor.execute(); } catch (ExcelAnalysisStopException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Custom stop!"); @@ -153,8 +153,8 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } @Override - public com.alibaba.excel.analysis.ExcelExecutor excelExecutor() { - return excelExecutor; + public ExcelReadExecutor excelExecutor() { + return excelReadExecutor; } @Override diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java similarity index 90% rename from src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java rename to src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java index 8868d2e..515eeb9 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelExecutor.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -9,7 +9,7 @@ import com.alibaba.excel.read.metadata.ReadSheet; * * @author Jiaju Zhuang */ -public interface ExcelExecutor { +public interface ExcelReadExecutor { /** * Returns the actual sheet in excel 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 36d2b74..7134e6f 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -22,7 +22,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; @@ -56,7 +56,7 @@ import com.alibaba.excel.util.CollectionUtils; * * @author jipengfei */ -public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor { +public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); private POIFSFileSystem poifsFileSystem; diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index 932fb65..7a34e6c 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -13,6 +13,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.StylesTable; @@ -24,7 +25,7 @@ import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.exception.ExcelAnalysisException; @@ -37,7 +38,7 @@ import com.alibaba.excel.util.FileUtils; * * @author jipengfei */ -public class XlsxSaxAnalyser implements ExcelExecutor { +public class XlsxSaxAnalyser implements ExcelReadExecutor { private AnalysisContext analysisContext; private List sheetList; @@ -135,7 +136,7 @@ public class XlsxSaxAnalyser implements ExcelExecutor { } else { FileUtils.writeToFile(tempFile, readWorkbookHolder.getInputStream()); } - return OPCPackage.open(tempFile); + return OPCPackage.open(tempFile, PackageAccess.READ); } @Override diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java index 7b8ba86..5c918f2 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -2,7 +2,7 @@ package com.alibaba.excel.context; import java.io.InputStream; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -22,12 +22,12 @@ public interface AnalysisContext { /** * Select the current table * - * @param excelExecutor + * @param excelReadExecutor * Excel file Executor * @param readSheet * sheet to read */ - void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet); + void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet); /** * All information about the workbook you are currently working on diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java index d9d7ded..999aa50 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -5,7 +5,7 @@ import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelExecutor; +import com.alibaba.excel.analysis.ExcelReadExecutor; import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.metadata.Sheet; @@ -53,13 +53,13 @@ public class AnalysisContextImpl implements AnalysisContext { } @Override - public void currentSheet(ExcelExecutor excelExecutor, ReadSheet readSheet) { + public void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet) { if (readSheet == null) { throw new IllegalArgumentException("Sheet argument cannot be null."); } readSheetHolder = new ReadSheetHolder(readSheet, readWorkbookHolder); currentReadHolder = readSheetHolder; - selectSheet(excelExecutor); + selectSheet(excelReadExecutor); if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { throw new ExcelAnalysisException("Cannot read sheet repeatedly."); } @@ -69,9 +69,9 @@ public class AnalysisContextImpl implements AnalysisContext { } } - private void selectSheet(ExcelExecutor excelExecutor) { - if (excelExecutor instanceof XlsxSaxAnalyser) { - selectSheet07(excelExecutor); + private void selectSheet(ExcelReadExecutor excelReadExecutor) { + if (excelReadExecutor instanceof XlsxSaxAnalyser) { + selectSheet07(excelReadExecutor); } else { selectSheet03(); } @@ -87,9 +87,9 @@ public class AnalysisContextImpl implements AnalysisContext { readSheetHolder.setSheetNo(0); } - private void selectSheet07(ExcelExecutor excelExecutor) { + private void selectSheet07(ExcelReadExecutor excelReadExecutor) { if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { + for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { if (readSheetExcel.getSheetNo().equals(readSheetHolder.getSheetNo())) { readSheetHolder.setSheetName(readSheetExcel.getSheetName()); return; @@ -98,7 +98,7 @@ public class AnalysisContextImpl implements AnalysisContext { throw new ExcelAnalysisException("Can not find sheet:" + readSheetHolder.getSheetNo()); } if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - for (ReadSheet readSheetExcel : excelExecutor.sheetList()) { + for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { String sheetName = readSheetExcel.getSheetName(); if (sheetName == null) { continue; @@ -112,7 +112,7 @@ public class AnalysisContextImpl implements AnalysisContext { } } } - ReadSheet readSheetExcel = excelExecutor.sheetList().get(0); + ReadSheet readSheetExcel = excelReadExecutor.sheetList().get(0); readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); readSheetHolder.setSheetName(readSheetExcel.getSheetName()); } diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java index 17db4ca..e561319 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ b/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -5,6 +5,7 @@ import java.io.OutputStream; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.holder.WriteHolder; @@ -21,9 +22,11 @@ public interface WriteContext { /** * If the current sheet already exists, select it; if not, create it * - * @param writeSheet Current sheet + * @param writeSheet + * Current sheet + * @param writeType */ - void currentSheet(WriteSheet writeSheet); + void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType); /** * If the current table already exists, select it; if not, create it diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 34b90ba..c71d08d 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -1,7 +1,6 @@ package com.alibaba.excel.context; import java.io.OutputStream; -import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; @@ -10,18 +9,15 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFSheet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.SheetWriteHandler; -import com.alibaba.excel.write.handler.WorkbookWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; @@ -65,42 +61,18 @@ public class WriteContextImpl implements WriteContext { LOGGER.debug("Begin to Initialization 'WriteContextImpl'"); } initCurrentWorkbookHolder(writeWorkbook); - beforeWorkbookCreate(); + WriteHandlerUtils.beforeWorkbookCreate(this); try { - writeWorkbookHolder.setWorkbook(WorkBookUtil.createWorkBook(writeWorkbookHolder)); + WorkBookUtil.createWorkBook(writeWorkbookHolder); } catch (Exception e) { throw new ExcelGenerateException("Create workbook failure", e); } - afterWorkbookCreate(); + WriteHandlerUtils.afterWorkbookCreate(this); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Initialization 'WriteContextImpl' complete"); } } - private void beforeWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); - } - } - } - - private void afterWorkbookCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(WorkbookWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof WorkbookWriteHandler) { - ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeWorkbookHolder); - } - } - } - private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) { writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook); currentWriteHolder = writeWorkbookHolder; @@ -113,7 +85,7 @@ public class WriteContextImpl implements WriteContext { * @param writeSheet */ @Override - public void currentSheet(WriteSheet writeSheet) { + public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) { if (writeSheet == null) { throw new IllegalArgumentException("Sheet argument cannot be null"); } @@ -137,38 +109,10 @@ public class WriteContextImpl implements WriteContext { return; } initCurrentSheetHolder(writeSheet); - beforeSheetCreate(); + WriteHandlerUtils.beforeSheetCreate(this); // Initialization current sheet - initSheet(); - afterSheetCreate(); - } - - private void beforeSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - } - - private void afterSheetCreate() { - List handlerList = currentWriteHolder.writeHandlerMap().get(SheetWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof SheetWriteHandler) { - ((SheetWriteHandler)writeHandler).afterSheetCreate(writeWorkbookHolder, writeSheetHolder); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().sheet(writeSheetHolder.getSheetNo(), - writeSheetHolder.getSheet()); - } + initSheet(writeType); + WriteHandlerUtils.afterSheetCreate(this); } private void initCurrentSheetHolder(WriteSheet writeSheet) { @@ -181,29 +125,24 @@ public class WriteContextImpl implements WriteContext { } } - private void initSheet() { - try { - if (writeWorkbookHolder.getXssfWorkbook() != null) { - writeSheetHolder - .setXssfSheet(writeWorkbookHolder.getXssfWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); - } - } catch (Exception e) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Can not find XSSFSheet:{}.", writeSheetHolder.getSheetNo()); - } - } + private void initSheet(WriteTypeEnum writeType) { Sheet currentSheet; try { currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo()); + writeSheetHolder + .setCachedSheet(writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo()); } currentSheet = WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(), writeSheetHolder.getSheetName()); + writeSheetHolder.setCachedSheet(currentSheet); } writeSheetHolder.setSheet(currentSheet); - // Initialization head - initHead(writeSheetHolder.excelWriteHeadProperty()); + if (WriteTypeEnum.ADD.equals(writeType)) { + // Initialization head + initHead(writeSheetHolder.excelWriteHeadProperty()); + } } public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) { @@ -216,42 +155,13 @@ public class WriteContextImpl implements WriteContext { addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex; i++, relativeRowIndex++) { - beforeRowCreate(newRowIndex, relativeRowIndex); + WriteHandlerUtils.beforeRowCreate(this, newRowIndex, relativeRowIndex, Boolean.TRUE); Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); - afterRowCreate(row, relativeRowIndex); + WriteHandlerUtils.afterRowCreate(this, row, relativeRowIndex, Boolean.TRUE); addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex); } } - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(writeSheetHolder, writeTableHolder, rowIndex, - relativeRowIndex, true); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(writeSheetHolder, writeTableHolder, row, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { writeSheetHolder.getSheet().addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, @@ -262,38 +172,13 @@ public class WriteContextImpl implements WriteContext { private void addOneRowOfHeadDataToExcel(Row row, Map headMap, int relativeRowIndex) { for (Map.Entry entry : headMap.entrySet()) { Head head = entry.getValue(); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, entry.getKey(), head.getHeadNameList().get(relativeRowIndex)); - afterCellCreate(head, cell, relativeRowIndex); - } - } - - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, - relativeRowIndex, true); - } - } - } - - private void afterCellCreate(Head head, Cell cell, int relativeRowIndex) { - List handlerList = currentWriteHolder.writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, - relativeRowIndex, true); - } - } - if (null != writeWorkbookHolder.getWriteWorkbook().getWriteHandler()) { - writeWorkbookHolder.getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + int columnIndex = entry.getKey(); + WriteHandlerUtils.beforeCellCreate(this, row, head, columnIndex, relativeRowIndex, Boolean.TRUE); + Cell cell = row.createCell(columnIndex); + WriteHandlerUtils.afterCellCreate(this, cell, head, relativeRowIndex, Boolean.TRUE); + cell.setCellValue(head.getHeadNameList().get(relativeRowIndex)); + CellData cellData = null; + WriteHandlerUtils.afterCellDispose(this, cellData, cell, head, relativeRowIndex, Boolean.TRUE); } } @@ -352,10 +237,10 @@ public class WriteContextImpl implements WriteContext { @Override public void finish() { + WriteHandlerUtils.afterWorkbookDispose(this); if (writeWorkbookHolder == null) { return; } - try { writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); writeWorkbookHolder.getWorkbook().close(); @@ -370,13 +255,6 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } - try { - if (writeWorkbookHolder.getTempTemplateInputStream() != null) { - writeWorkbookHolder.getTempTemplateInputStream().close(); - } - } catch (Throwable t) { - throwCanNotCloseIo(t); - } try { if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { writeWorkbookHolder.getOutputStream().close(); @@ -385,9 +263,8 @@ public class WriteContextImpl implements WriteContext { throwCanNotCloseIo(t); } try { - if (!writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getFile() != null - && writeWorkbookHolder.getOutputStream() != null) { - writeWorkbookHolder.getOutputStream().close(); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeWorkbookHolder.getTempTemplateInputStream().close(); } } catch (Throwable t) { throwCanNotCloseIo(t); diff --git a/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java new file mode 100644 index 0000000..78803f1 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Direction of writing + * + * @author Jiaju Zhuang + **/ +public enum WriteDirectionEnum { + /** + * Vertical write. + */ + VERTICAL, + /** + * Horizontal write. + */ + HORIZONTAL,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java similarity index 91% rename from src/main/java/com/alibaba/excel/enums/WriteLastRowType.java rename to src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java index bd477bd..d9a41ae 100644 --- a/src/main/java/com/alibaba/excel/enums/WriteLastRowType.java +++ b/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java @@ -5,7 +5,7 @@ package com.alibaba.excel.enums; * * @author Jiaju Zhuang **/ -public enum WriteLastRowType { +public enum WriteLastRowTypeEnum { /** * Excel are created without templates ,And any data has been written; */ diff --git a/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java new file mode 100644 index 0000000..e9d22a8 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Type of template to read when writing + * + * @author Jiaju Zhuang + **/ +public enum WriteTemplateAnalysisCellTypeEnum { + /** + * Common field. + */ + COMMON, + /** + * A collection of fields. + */ + COLLECTION,; +} diff --git a/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java new file mode 100644 index 0000000..9dbbd31 --- /dev/null +++ b/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Enumeration of write methods + * + * @author Jiaju Zhuang + **/ +public enum WriteTypeEnum { + /** + * Add. + */ + ADD, + /** + * Fill. + */ + FILL,; +} 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 a1200d5..09d5560 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -52,6 +52,10 @@ public class ExcelHeadProperty { * Configuration column information */ private Map contentPropertyMap; + /** + * Configuration column information + */ + private Map fieldNameContentPropertyMap; /** * Fields ignored */ @@ -61,6 +65,7 @@ public class ExcelHeadProperty { this.headClazz = headClazz; headMap = new TreeMap(); contentPropertyMap = new TreeMap(); + fieldNameContentPropertyMap = new HashMap(); ignoreMap = new HashMap(16); headKind = HeadKindEnum.NONE; headRowNumber = 0; @@ -78,10 +83,6 @@ public class ExcelHeadProperty { if (LOGGER.isDebugEnabled()) { LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind); } - if (!hasHead()) { - LOGGER.warn( - "The table has no header set and all annotations will not be read.If you want to use annotations, please use set head class in ExcelWriterBuilder/ExcelWriterSheetBuilder/ExcelWriterTableBuilder"); - } } private void initHeadRowNumber() { @@ -190,6 +191,7 @@ public class ExcelHeadProperty { .setNumberFormatProperty(NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); headMap.put(index, head); contentPropertyMap.put(index, excelContentProperty); + fieldNameContentPropertyMap.put(field.getName(), excelContentProperty); } public Class getHeadClazz() { @@ -236,6 +238,14 @@ public class ExcelHeadProperty { this.contentPropertyMap = contentPropertyMap; } + public Map getFieldNameContentPropertyMap() { + return fieldNameContentPropertyMap; + } + + public void setFieldNameContentPropertyMap(Map fieldNameContentPropertyMap) { + this.fieldNameContentPropertyMap = fieldNameContentPropertyMap; + } + public Map getIgnoreMap() { return ignoreMap; } diff --git a/src/main/java/com/alibaba/excel/util/StringUtils.java b/src/main/java/com/alibaba/excel/util/StringUtils.java index 25ed127..948ae65 100644 --- a/src/main/java/com/alibaba/excel/util/StringUtils.java +++ b/src/main/java/com/alibaba/excel/util/StringUtils.java @@ -6,10 +6,11 @@ package com.alibaba.excel.util; * @author jipengfei */ public class StringUtils { + public static final String EMPTY = ""; private StringUtils() {} public static boolean isEmpty(Object str) { - return (str == null || "".equals(str)); + return (str == null || EMPTY.equals(str)); } } diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index 78cbd27..949c3da 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -2,6 +2,7 @@ package com.alibaba.excel.util; import java.io.IOException; +import org.apache.poi.hssf.usermodel.HSSFFont; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -22,36 +23,31 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; */ public class WorkBookUtil { + private static final int ROW_ACCESS_WINDOW_SIZE = 500; + private WorkBookUtil() {} - public static Workbook createWorkBook(WriteWorkbookHolder writeWorkbookHolder) - throws IOException, InvalidFormatException { + public static void createWorkBook(WriteWorkbookHolder writeWorkbookHolder) throws IOException { if (ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { - XSSFWorkbook xssfWorkbook = null; - if (writeWorkbookHolder.getTemplateFile() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateFile()); - } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateInputStream()); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream()); + writeWorkbookHolder.setCachedWorkbook(xssfWorkbook); + writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE)); + return; } - // When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we - // are using the template, so we cache it - if (xssfWorkbook != null) { - writeWorkbookHolder.setXssfWorkbook(xssfWorkbook); - for (int i = 0; i < xssfWorkbook.getNumberOfSheets(); i++) { - writeWorkbookHolder.getTemplateLastRowMap().put(i, xssfWorkbook.getSheetAt(i).getLastRowNum()); - } - return new SXSSFWorkbook(xssfWorkbook); - } - return new SXSSFWorkbook(500); - } - if (writeWorkbookHolder.getTemplateFile() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateFile())); + SXSSFWorkbook sxssWorkbook = new SXSSFWorkbook(ROW_ACCESS_WINDOW_SIZE); + writeWorkbookHolder.setCachedWorkbook(sxssWorkbook); + writeWorkbookHolder.setWorkbook(sxssWorkbook); + return; } - if (writeWorkbookHolder.getTemplateInputStream() != null) { - return new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTemplateInputStream())); + HSSFWorkbook hssfWorkbook; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + hssfWorkbook = new HSSFWorkbook(new POIFSFileSystem(writeWorkbookHolder.getTempTemplateInputStream())); + } else { + hssfWorkbook = new HSSFWorkbook(); } - return new HSSFWorkbook(); + writeWorkbookHolder.setCachedWorkbook(hssfWorkbook); + writeWorkbookHolder.setWorkbook(hssfWorkbook); } public static Sheet createSheet(Workbook workbook, String sheetName) { diff --git a/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java new file mode 100644 index 0000000..b303a31 --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java @@ -0,0 +1,196 @@ +package com.alibaba.excel.util; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.WriteHandler; + +/** + * Write handler utils + * + * @author Jiaju Zhuang + */ +public class WriteHandlerUtils { + + private WriteHandlerUtils() {} + + public static void beforeWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).beforeWorkbookCreate(); + } + } + } + + public static void afterWorkbookCreate(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookCreate(writeContext.writeWorkbookHolder()); + } + } + } + + public static void afterWorkbookDispose(WriteContext writeContext) { + List handlerList = + writeContext.writeWorkbookHolder().writeHandlerMap().get(WorkbookWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof WorkbookWriteHandler) { + ((WorkbookWriteHandler)writeHandler).afterWorkbookDispose(writeContext.writeWorkbookHolder()); + } + } + } + + public static void beforeSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).beforeSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + } + + public static void afterSheetCreate(WriteContext writeContext) { + List handlerList = writeContext.writeSheetHolder().writeHandlerMap().get(SheetWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof SheetWriteHandler) { + ((SheetWriteHandler)writeHandler).afterSheetCreate(writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler() + .sheet(writeContext.writeSheetHolder().getSheetNo(), writeContext.writeSheetHolder().getSheet()); + } + } + + public static void beforeCellCreate(WriteContext writeContext, Row row, Head head, Integer columnIndex, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).beforeCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, head, columnIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterCellCreate(WriteContext writeContext, Cell cell, Head head, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cell, head, relativeRowIndex, isHead); + } + } + } + + public static void afterCellDispose(WriteContext writeContext, CellData cellData, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List cellDataList = new ArrayList(); + if (cell != null) { + cellDataList.add(cellData); + } + afterCellDispose(writeContext, cellDataList, cell, head, relativeRowIndex, isHead); + } + + public static void afterCellDispose(WriteContext writeContext, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + List handlerList = + writeContext.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof CellWriteHandler) { + ((CellWriteHandler)writeHandler).afterCellDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), cellDataList, cell, head, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); + } + } + + public static void beforeRowCreate(WriteContext writeContext, Integer rowIndex, Integer relativeRowIndex, + Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).beforeRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), rowIndex, relativeRowIndex, isHead); + } + } + } + + public static void afterRowCreate(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowCreate(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + + } + + public static void afterRowDispose(WriteContext writeContext, Row row, Integer relativeRowIndex, Boolean isHead) { + List handlerList = writeContext.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); + if (handlerList == null || handlerList.isEmpty()) { + return; + } + for (WriteHandler writeHandler : handlerList) { + if (writeHandler instanceof RowWriteHandler) { + ((RowWriteHandler)writeHandler).afterRowDispose(writeContext.writeSheetHolder(), + writeContext.writeTableHolder(), row, relativeRowIndex, isHead); + } + } + if (null != writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { + writeContext.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); + } + } +} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java index 6dcf947..8a60444 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -6,6 +6,7 @@ import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei @@ -40,9 +41,10 @@ public interface ExcelBuilder { * WorkBook fill value * * @param data + * @param fillConfig * @param writeSheet */ - void fill(Object data, WriteSheet writeSheet); + void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet); /** * Creates new cell range. Indexes are zero-based. diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 3bbda25..3d8067a 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -1,63 +1,29 @@ package com.alibaba.excel.write; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.ClientAnchor; -import org.apache.poi.ss.usermodel.CreationHelper; -import org.apache.poi.ss.usermodel.Drawing; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContextImpl; -import com.alibaba.excel.converters.Converter; -import com.alibaba.excel.converters.ConverterKeyBuild; -import com.alibaba.excel.enums.CellDataTypeEnum; -import com.alibaba.excel.enums.HeadKindEnum; -import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.enums.WriteTypeEnum; import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.CellData; -import com.alibaba.excel.metadata.Head; -import com.alibaba.excel.metadata.property.ExcelContentProperty; -import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; -import com.alibaba.excel.util.WorkBookUtil; -import com.alibaba.excel.write.handler.CellWriteHandler; -import com.alibaba.excel.write.handler.RowWriteHandler; -import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.executor.ExcelWriteAddExecutor; +import com.alibaba.excel.write.executor.ExcelWriteFillExecutor; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.WriteWorkbook; -import com.alibaba.excel.write.metadata.fill.AnalysisCell; -import com.alibaba.excel.write.metadata.holder.WriteHolder; -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; - -import net.sf.cglib.beans.BeanMap; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * @author jipengfei */ public class ExcelBuilderImpl implements ExcelBuilder { - private static final String FILL_PREFIX = "${"; - private static final String FILL_SUFFIX = "}"; - private static final Pattern FILL_PATTERN = Pattern.compile("\\$\\{[^}]+}"); - private WriteContext context; + private ExcelWriteFillExecutor excelWriteFillExecutor; + private ExcelWriteAddExecutor excelWriteAddExecutor; public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { try { @@ -73,23 +39,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void doAddContent(List data) { - if (CollectionUtils.isEmpty(data)) { - return; - } - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); - if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { - newRowIndex += context.currentWriteHolder().relativeHeadRowIndex(); - } - // BeanMap is out of order,so use fieldList - List fieldList = new ArrayList(); - for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { - int n = relativeRowIndex + newRowIndex; - addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); - } - } - @Override public void addContent(List data, WriteSheet writeSheet) { addContent(data, writeSheet, null); @@ -98,9 +47,15 @@ public class ExcelBuilderImpl implements ExcelBuilder { @Override public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable) { try { - context.currentSheet(writeSheet); + if (data == null) { + return; + } + context.currentSheet(writeSheet, WriteTypeEnum.ADD); context.currentTable(writeTable); - doAddContent(data); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); + } + excelWriteAddExecutor.add(data); } catch (RuntimeException e) { finish(); throw e; @@ -111,14 +66,19 @@ public class ExcelBuilderImpl implements ExcelBuilder { } @Override - public void fill(Object data, WriteSheet writeSheet) { + public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { try { - if (context.writeWorkbookHolder().getTemplateFile() == null - && context.writeWorkbookHolder().getTemplateInputStream() == null) { + if (data == null) { + return; + } + if (context.writeWorkbookHolder().getTempTemplateInputStream() == null) { throw new ExcelGenerateException("Calling the 'fill' method must use a template."); } - context.currentSheet(writeSheet); - doFill(data); + context.currentSheet(writeSheet, WriteTypeEnum.FILL); + if (excelWriteFillExecutor == null) { + excelWriteFillExecutor = new ExcelWriteFillExecutor(context); + } + excelWriteFillExecutor.fill(data, fillConfig); } catch (RuntimeException e) { finish(); throw e; @@ -128,73 +88,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void doFill(Object data) { - WriteSheetHolder writeSheetHolder = context.writeSheetHolder(); - XSSFSheet sheet = writeSheetHolder.getXssfSheet(); - Map templateLastRowMap = context.writeWorkbookHolder().getTemplateLastRowMap(); - if (sheet == null) { - throw new ExcelGenerateException( - "The corresponding table cannot be found,sheetNo:" + writeSheetHolder.getSheetNo()); - } - List analysisCellList = new ArrayList(); - for (int i = 0; i <= sheet.getLastRowNum(); i++) { - Row row = sheet.getRow(i); - for (int j = 0; j < row.getLastCellNum(); j++) { - Cell cell = row.getCell(j); - String value = cell.getStringCellValue(); - if (FILL_PATTERN.matcher(value).matches()) { - AnalysisCell analysisCell = new AnalysisCell(); - analysisCell.setRowIndex(i); - analysisCell.setColumnIndex(j); - List variableList = new ArrayList(); - analysisCell.setVariableList(variableList); - boolean matches = true; - int index = 0; - while (matches) { - Matcher matcher = FILL_PATTERN.matcher(value); - String variable = value.substring(matcher.regionStart() + 2, matcher.regionEnd() - 1); - variableList.add(variable); - value = matcher.replaceFirst("{" + index++ + "}"); - matches = FILL_PATTERN.matcher(value).matches(); - analysisCellList.add(analysisCell); - } - } - } - } - - if (data instanceof Collection) { - - } else if (data instanceof Map) { - - } else { - - } - BeanMap beanMap = BeanMap.create(data); - - for (AnalysisCell analysisCell : analysisCellList) { - Cell cell = sheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); - if (analysisCell.getVariableList().size() == 1) { - Object value = beanMap.get(analysisCell.getVariableList().get(0)); - if (value == null) { - continue; - } - converterAndSet(writeSheetHolder, value.getClass(), cell, value, null); - } else { - List fileDataStringList = new ArrayList(); - for (String variable : analysisCell.getVariableList()) { - Object value = beanMap.get(variable); - CellData cellData = convert(writeSheetHolder, String.class, cell, value, null); - String fillDataString = cellData.getStringValue(); - if (fillDataString == null) { - fillDataString = ""; - } - fileDataStringList.add(fillDataString); - } - cell.setCellValue(String.format(analysisCell.getPrepareData(), fileDataStringList)); - } - } - } - @Override public void finish() { if (context != null) { @@ -212,286 +105,4 @@ public class ExcelBuilderImpl implements ExcelBuilder { public WriteContext writeContext() { return context; } - - private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { - beforeRowCreate(n, relativeRowIndex); - Row row = WorkBookUtil.createRow(context.writeSheetHolder().getSheet(), n); - afterRowCreate(row, relativeRowIndex); - if (oneRowData instanceof List) { - addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); - } else { - addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); - } - } - - private void beforeRowCreate(int rowIndex, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).beforeRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - rowIndex, relativeRowIndex, false); - } - } - } - - private void afterRowCreate(Row row, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(RowWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof RowWriteHandler) { - ((RowWriteHandler)writeHandler).afterRowCreate(context.writeSheetHolder(), context.writeTableHolder(), - row, relativeRowIndex, false); - } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().row(row.getRowNum(), row); - } - } - - private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { - if (CollectionUtils.isEmpty(oneRowData)) { - return; - } - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - int dataIndex = 0; - int cellIndex = 0; - for (Map.Entry entry : headMap.entrySet()) { - if (dataIndex >= oneRowData.size()) { - return; - } - cellIndex = entry.getKey(); - Head head = entry.getValue(); - doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); - } - // Finish - if (dataIndex >= oneRowData.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - int size = oneRowData.size() - dataIndex; - for (int i = 0; i < size; i++) { - doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); - } - } - - private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, - int cellIndex) { - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = oneRowData.get(dataIndex); - CellData cellData = - converterAndSet(context.currentWriteHolder(), value == null ? null : value.getClass(), cell, value, null); - afterCellCreate(head, cellData, cell, relativeRowIndex); - } - - private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { - WriteHolder currentWriteHolder = context.currentWriteHolder(); - BeanMap beanMap = BeanMap.create(oneRowData); - Set beanMapHandledSet = new HashSet(); - int cellIndex = 0; - // If it's a class it needs to be cast by type - if (HeadKindEnum.CLASS.equals(context.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { - Map headMap = context.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); - Map contentPropertyMap = - context.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); - for (Map.Entry entry : contentPropertyMap.entrySet()) { - cellIndex = entry.getKey(); - ExcelContentProperty excelContentProperty = entry.getValue(); - String name = excelContentProperty.getField().getName(); - if (!beanMap.containsKey(name)) { - continue; - } - Head head = headMap.get(cellIndex); - beforeCellCreate(row, head, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex); - Object value = beanMap.get(name); - CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, - value, excelContentProperty); - afterCellCreate(head, cellData, cell, relativeRowIndex); - beanMapHandledSet.add(name); - } - } - // Finish - if (beanMapHandledSet.size() == beanMap.size()) { - return; - } - if (cellIndex != 0) { - cellIndex++; - } - Map ignoreMap = context.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); - initFieldList(oneRowData.getClass(), fieldList); - for (Field field : fieldList) { - String filedName = field.getName(); - boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) - || ignoreMap.containsKey(filedName); - if (uselessData) { - continue; - } - Object value = beanMap.get(filedName); - if (value == null) { - continue; - } - beforeCellCreate(row, null, relativeRowIndex); - Cell cell = WorkBookUtil.createCell(row, cellIndex++); - CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); - afterCellCreate(null, cellData, cell, relativeRowIndex); - } - } - - private void initFieldList(Class clazz, List fieldList) { - if (!fieldList.isEmpty()) { - return; - } - Class tempClass = clazz; - while (tempClass != null) { - if (tempClass != BaseRowModel.class) { - Collections.addAll(fieldList, tempClass.getDeclaredFields()); - } - tempClass = tempClass.getSuperclass(); - } - } - - private void beforeCellCreate(Row row, Head head, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).beforeCellCreate(context.writeSheetHolder(), - context.writeTableHolder(), row, head, relativeRowIndex, false); - } - } - - } - - private void afterCellCreate(Head head, CellData cellData, Cell cell, int relativeRowIndex) { - List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); - if (handlerList == null || handlerList.isEmpty()) { - return; - } - for (WriteHandler writeHandler : handlerList) { - if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(context.writeSheetHolder(), context.writeTableHolder(), - cellData, cell, head, relativeRowIndex, false); - } - } - if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { - context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler().cell(cell.getRowIndex(), cell); - } - } - - private CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - if (value == null) { - return new CellData(); - } - if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { - value = ((String)value).trim(); - } - CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); - if (cellData.getFormula() != null && cellData.getFormula()) { - cell.setCellFormula(cellData.getFormulaValue()); - } - switch (cellData.getType()) { - case STRING: - cell.setCellValue(cellData.getStringValue()); - return cellData; - case BOOLEAN: - cell.setCellValue(cellData.getBooleanValue()); - return cellData; - case NUMBER: - cell.setCellValue(cellData.getNumberValue().doubleValue()); - return cellData; - case IMAGE: - setImageValue(cellData, cell); - return cellData; - case EMPTY: - return cellData; - default: - throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() - + "at row:" + cell.getRow().getRowNum()); - } - } - - private void setImageValue(CellData cellData, Cell cell) { - Sheet sheet = cell.getSheet(); - int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); - Drawing drawing = sheet.getDrawingPatriarch(); - if (drawing == null) { - drawing = sheet.createDrawingPatriarch(); - } - CreationHelper helper = sheet.getWorkbook().getCreationHelper(); - ClientAnchor anchor = helper.createClientAnchor(); - anchor.setDx1(0); - anchor.setDx2(0); - anchor.setDy1(0); - anchor.setDy2(0); - anchor.setCol1(cell.getColumnIndex()); - anchor.setCol2(cell.getColumnIndex() + 1); - anchor.setRow1(cell.getRowIndex()); - anchor.setRow2(cell.getRowIndex() + 1); - anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); - drawing.createPicture(anchor, index); - } - - private CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - // This means that the user has defined the data. - if (value instanceof CellData) { - CellData cellDataValue = (CellData)value; - if (cellDataValue.getType() != null) { - return cellDataValue; - } else { - if (cellDataValue.getData() == null) { - cellDataValue.setType(CellDataTypeEnum.EMPTY); - return cellDataValue; - } - } - CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, - cellDataValue.getData(), excelContentProperty); - // The formula information is subject to user input - if (cellDataValue.getFormula() != null) { - cellDataReturn.setFormula(cellDataValue.getFormula()); - cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); - } - return cellDataReturn; - } - return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); - } - - private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, - ExcelContentProperty excelContentProperty) { - Converter converter = null; - if (excelContentProperty != null) { - converter = excelContentProperty.getConverter(); - } - if (converter == null) { - converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); - } - if (converter == null) { - throw new ExcelDataConvertException( - "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); - } - CellData cellData; - try { - cellData = - converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); - } catch (Exception e) { - throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), - e); - } - if (cellData == null || cellData.getType() == null) { - throw new ExcelDataConvertException( - "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); - } - return cellData; - } } 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 29e8277..8a9e0c6 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -8,6 +8,7 @@ import com.alibaba.excel.converters.Converter; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; /** * Build sheet @@ -136,10 +137,14 @@ public class ExcelWriterSheetBuilder { } public void doFill(Object data) { + doFill(data, null); + } + + public void doFill(Object data, FillConfig fillConfig) { if (excelWriter == null) { throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); } - excelWriter.fill(data, build()); + excelWriter.fill(data, fillConfig, build()); excelWriter.finish(); } diff --git a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java new file mode 100644 index 0000000..9adcb62 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java @@ -0,0 +1,140 @@ +package com.alibaba.excel.write.executor; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.metadata.holder.WriteHolder; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { + protected WriteContext writeContext; + + public AbstractExcelWriteExecutor(WriteContext writeContext) { + this.writeContext = writeContext; + } + + protected CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { + value = ((String)value).trim(); + } + CellData cellData = convert(currentWriteHolder, clazz, cell, value, excelContentProperty); + if (cellData.getFormula() != null && cellData.getFormula()) { + cell.setCellFormula(cellData.getFormulaValue()); + } + switch (cellData.getType()) { + case STRING: + cell.setCellValue(cellData.getStringValue()); + return cellData; + case BOOLEAN: + cell.setCellValue(cellData.getBooleanValue()); + return cellData; + case NUMBER: + cell.setCellValue(cellData.getNumberValue().doubleValue()); + return cellData; + case IMAGE: + setImageValue(cellData, cell); + return cellData; + case EMPTY: + return cellData; + default: + throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + + "at row:" + cell.getRow().getRowNum()); + } + } + + protected CellData convert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + if (value == null) { + return new CellData(); + } + // This means that the user has defined the data. + if (value instanceof CellData) { + CellData cellDataValue = (CellData)value; + if (cellDataValue.getType() != null) { + return cellDataValue; + } else { + if (cellDataValue.getData() == null) { + cellDataValue.setType(CellDataTypeEnum.EMPTY); + return cellDataValue; + } + } + CellData cellDataReturn = doConvert(currentWriteHolder, cellDataValue.getData().getClass(), cell, + cellDataValue.getData(), excelContentProperty); + // The formula information is subject to user input + if (cellDataValue.getFormula() != null) { + cellDataReturn.setFormula(cellDataValue.getFormula()); + cellDataReturn.setFormulaValue(cellDataValue.getFormulaValue()); + } + return cellDataReturn; + } + return doConvert(currentWriteHolder, clazz, cell, value, excelContentProperty); + } + + private CellData doConvert(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + ExcelContentProperty excelContentProperty) { + Converter converter = null; + if (excelContentProperty != null) { + converter = excelContentProperty.getConverter(); + } + if (converter == null) { + converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz)); + } + if (converter == null) { + throw new ExcelDataConvertException( + "Can not find 'Converter' support class " + clazz.getSimpleName() + "."); + } + CellData cellData; + try { + cellData = + converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); + } catch (Exception e) { + throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), + e); + } + if (cellData == null || cellData.getType() == null) { + throw new ExcelDataConvertException( + "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum()); + } + return cellData; + } + + private void setImageValue(CellData cellData, Cell cell) { + Sheet sheet = cell.getSheet(); + int index = sheet.getWorkbook().addPicture(cellData.getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG); + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + CreationHelper helper = sheet.getWorkbook().getCreationHelper(); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setCol2(cell.getColumnIndex() + 1); + anchor.setRow1(cell.getRowIndex()); + anchor.setRow2(cell.getRowIndex() + 1); + anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); + drawing.createPicture(anchor, index); + } +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java new file mode 100644 index 0000000..8843e2c --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java @@ -0,0 +1,179 @@ +package com.alibaba.excel.write.executor; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.BaseRowModel; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Add the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { + + public ExcelWriteAddExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void add(List data) { + if (CollectionUtils.isEmpty(data)) { + return; + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { + newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex(); + } + // BeanMap is out of order,so use fieldList + List fieldList = new ArrayList(); + for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) { + int n = relativeRowIndex + newRowIndex; + addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList); + } + } + + private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex, List fieldList) { + if (oneRowData == null) { + return; + } + WriteHandlerUtils.beforeRowCreate(writeContext, n, relativeRowIndex, Boolean.FALSE); + Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), n); + WriteHandlerUtils.afterRowCreate(writeContext, row, relativeRowIndex, Boolean.FALSE); + if (oneRowData instanceof List) { + addBasicTypeToExcel((List)oneRowData, row, relativeRowIndex); + } else { + addJavaObjectToExcel(oneRowData, row, relativeRowIndex, fieldList); + } + WriteHandlerUtils.afterRowDispose(writeContext, row, relativeRowIndex, Boolean.FALSE); + } + + private void addBasicTypeToExcel(List oneRowData, Row row, int relativeRowIndex) { + if (CollectionUtils.isEmpty(oneRowData)) { + return; + } + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + int dataIndex = 0; + int cellIndex = 0; + for (Map.Entry entry : headMap.entrySet()) { + if (dataIndex >= oneRowData.size()) { + return; + } + cellIndex = entry.getKey(); + Head head = entry.getValue(); + doAddBasicTypeToExcel(oneRowData, head, row, relativeRowIndex, dataIndex++, cellIndex); + } + // Finish + if (dataIndex >= oneRowData.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + int size = oneRowData.size() - dataIndex; + for (int i = 0; i < size; i++) { + doAddBasicTypeToExcel(oneRowData, null, row, relativeRowIndex, dataIndex++, cellIndex++); + } + } + + private void doAddBasicTypeToExcel(List oneRowData, Head head, Row row, int relativeRowIndex, int dataIndex, + int cellIndex) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = oneRowData.get(dataIndex); + CellData cellData = converterAndSet(writeContext.currentWriteHolder(), value == null ? null : value.getClass(), + cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + } + + private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { + WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); + BeanMap beanMap = BeanMap.create(oneRowData); + Set beanMapHandledSet = new HashSet(); + int cellIndex = 0; + // If it's a class it needs to be cast by type + if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + Map contentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap(); + for (Map.Entry entry : contentPropertyMap.entrySet()) { + cellIndex = entry.getKey(); + ExcelContentProperty excelContentProperty = entry.getValue(); + String name = excelContentProperty.getField().getName(); + if (!beanMap.containsKey(name)) { + continue; + } + Head head = headMap.get(cellIndex); + WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE); + Object value = beanMap.get(name); + CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, + value, excelContentProperty); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE); + beanMapHandledSet.add(name); + } + } + // Finish + if (beanMapHandledSet.size() == beanMap.size()) { + return; + } + if (cellIndex != 0) { + cellIndex++; + } + Map ignoreMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getIgnoreMap(); + initFieldList(oneRowData.getClass(), fieldList); + for (Field field : fieldList) { + String filedName = field.getName(); + boolean uselessData = !beanMap.containsKey(filedName) || beanMapHandledSet.contains(filedName) + || ignoreMap.containsKey(filedName); + if (uselessData) { + continue; + } + Object value = beanMap.get(filedName); + if (value == null) { + continue; + } + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); + Cell cell = WorkBookUtil.createCell(row, cellIndex++); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); + CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE); + } + } + + private void initFieldList(Class clazz, List fieldList) { + if (!fieldList.isEmpty()) { + return; + } + Class tempClass = clazz; + while (tempClass != null) { + if (tempClass != BaseRowModel.class) { + Collections.addAll(fieldList, tempClass.getDeclaredFields()); + } + tempClass = tempClass.getSuperclass(); + } + } + +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java new file mode 100644 index 0000000..b7cb607 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.write.executor; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelWriteExecutor { +} diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java new file mode 100644 index 0000000..217f21e --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -0,0 +1,377 @@ +package com.alibaba.excel.write.executor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.metadata.fill.AnalysisCell; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import net.sf.cglib.beans.BeanMap; + +/** + * Fill the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { + + private static final String ESCAPE_FILL_PREFIX = "\\\\\\{"; + private static final String ESCAPE_FILL_SUFFIX = "\\\\\\}"; + private static final String FILL_PREFIX = "{"; + private static final String FILL_SUFFIX = "}"; + private static final char IGNORE_CHAR = '\\'; + private static final String COLLECTION_PREFIX = "."; + /** + * Fields to replace in the template + */ + private Map> templateAnalysisCache = new HashMap>(8); + /** + * Collection fields to replace in the template + */ + private Map> templateCollectionAnalysisCache = + new HashMap>(8); + /** + * Style cache for collection fields + */ + private Map> collectionFieldStyleCache = + new HashMap>(8); + /** + * Last index cache for collection fields + */ + private Map> collectionLastIndexCache = + new HashMap>(8); + + public ExcelWriteFillExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void fill(Object data, FillConfig fillConfig) { + if (fillConfig == null) { + fillConfig = FillConfig.builder().build(true); + } + fillConfig.init(); + if (data instanceof Collection) { + List analysisCellList = readTemplateData(templateCollectionAnalysisCache); + Collection collectionData = (Collection)data; + if (CollectionUtils.isEmpty(collectionData)) { + return; + } + Iterator iterator = collectionData.iterator(); + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { + shiftRows(collectionData.size(), analysisCellList); + } + while (iterator.hasNext()) { + doFill(analysisCellList, iterator.next(), fillConfig); + } + } else { + doFill(readTemplateData(templateAnalysisCache), data, fillConfig); + } + } + + private void shiftRows(int size, List analysisCellList) { + if (CollectionUtils.isEmpty(analysisCellList)) { + return; + } + int maxRowIndex = 0; + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + for (AnalysisCell analysisCell : analysisCellList) { + if (collectionLastIndexMap != null) { + Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex != null) { + if (lastRowIndex > maxRowIndex) { + maxRowIndex = lastRowIndex; + } + continue; + } + } + if (analysisCell.getRowIndex() > maxRowIndex) { + maxRowIndex = analysisCell.getRowIndex(); + } + } + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + int lastRowIndex = cachedSheet.getLastRowNum(); + if (maxRowIndex >= lastRowIndex) { + return; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + int number = size; + if (collectionLastIndexMap == null) { + number--; + } + sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number); + for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { + if (analysisCell.getRowIndex() > maxRowIndex) { + analysisCell.setRowIndex(analysisCell.getRowIndex() + number); + } + } + } + + private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig) { + Map dataMap; + if (oneRowData instanceof Map) { + dataMap = (Map)oneRowData; + } else { + dataMap = BeanMap.create(oneRowData); + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + Map fieldNameContentPropertyMap = + writeContext.currentWriteHolder().excelWriteHeadProperty().getFieldNameContentPropertyMap(); + for (AnalysisCell analysisCell : analysisCellList) { + Cell cell = getOneCell(analysisCell, fillConfig); + if (analysisCell.getOnlyOneVariable()) { + String variable = analysisCell.getVariableList().get(0); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = converterAndSet(writeSheetHolder, value == null ? null : value.getClass(), cell, + value, fieldNameContentPropertyMap.get(variable)); + WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, null, Boolean.FALSE); + } else { + StringBuilder cellValueBuild = new StringBuilder(); + int index = 0; + List cellDataList = new ArrayList(); + for (String variable : analysisCell.getVariableList()) { + cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); + if (!dataMap.containsKey(variable)) { + continue; + } + Object value = dataMap.get(variable); + CellData cellData = convert(writeSheetHolder, value == null ? null : value.getClass(), cell, value, + fieldNameContentPropertyMap.get(variable)); + cellDataList.add(cellData); + switch (cellData.getType()) { + case STRING: + cellValueBuild.append(cellData.getStringValue()); + break; + case BOOLEAN: + cellValueBuild.append(cellData.getBooleanValue()); + break; + case NUMBER: + cellValueBuild.append(cellData.getNumberValue()); + break; + default: + break; + } + } + cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); + cell.setCellValue(cellValueBuild.toString()); + WriteHandlerUtils.afterCellDispose(writeContext, cellDataList, cell, null, null, Boolean.FALSE); + } + } + } + + private Cell getOneCell(AnalysisCell analysisCell, FillConfig fillConfig) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + return cachedSheet.getRow(analysisCell.getRowIndex()).getCell(analysisCell.getColumnIndex()); + } + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + Map collectionLastIndexMap = collectionLastIndexCache.get(sheetNo); + if (collectionLastIndexMap == null) { + collectionLastIndexMap = new HashMap(16); + collectionLastIndexCache.put(sheetNo, collectionLastIndexMap); + } + boolean isOriginalCell = false; + Integer lastRowIndex; + Integer lastColumnIndex; + switch (fillConfig.getDirection()) { + case VERTICAL: + lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex == null) { + lastRowIndex = analysisCell.getRowIndex(); + collectionLastIndexMap.put(analysisCell, lastRowIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastRowIndex); + } + lastColumnIndex = analysisCell.getColumnIndex(); + break; + case HORIZONTAL: + lastRowIndex = analysisCell.getRowIndex(); + lastColumnIndex = collectionLastIndexMap.get(analysisCell); + if (lastColumnIndex == null) { + lastColumnIndex = analysisCell.getColumnIndex(); + collectionLastIndexMap.put(analysisCell, lastColumnIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastColumnIndex); + } + break; + default: + throw new ExcelGenerateException("The wrong direction."); + } + Row row = sheet.getRow(lastRowIndex); + if (row == null) { + row = cachedSheet.getRow(lastRowIndex); + if (row == null) { + WriteHandlerUtils.beforeRowCreate(writeContext, lastRowIndex, null, Boolean.FALSE); + if (fillConfig.getForceNewRow()) { + row = cachedSheet.createRow(lastRowIndex); + } else { + row = sheet.createRow(lastRowIndex); + } + WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); + } + } + Cell cell = row.getCell(lastColumnIndex); + if (cell == null) { + WriteHandlerUtils.beforeCellCreate(writeContext, row, null, lastColumnIndex, null, Boolean.FALSE); + cell = row.createCell(lastColumnIndex); + WriteHandlerUtils.afterCellCreate(writeContext, cell, null, null, Boolean.FALSE); + } + + Map collectionFieldStyleMap = collectionFieldStyleCache.get(sheetNo); + if (collectionFieldStyleMap == null) { + collectionFieldStyleMap = new HashMap(16); + collectionFieldStyleCache.put(sheetNo, collectionFieldStyleMap); + } + if (isOriginalCell) { + collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + } else { + CellStyle cellStyle = collectionFieldStyleMap.get(analysisCell); + if (cellStyle != null) { + cell.setCellStyle(cellStyle); + } + } + return cell; + } + + private List readTemplateData(Map> analysisCache) { + Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); + List analysisCellList = analysisCache.get(sheetNo); + if (analysisCellList != null) { + return analysisCellList; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + analysisCellList = new ArrayList(); + List collectionAnalysisCellList = new ArrayList(); + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + if (cell == null) { + continue; + } + prepareData(cell.getStringCellValue(), analysisCellList, collectionAnalysisCellList, i, j); + } + } + templateAnalysisCache.put(sheetNo, analysisCellList); + templateCollectionAnalysisCache.put(sheetNo, collectionAnalysisCellList); + return analysisCache.get(sheetNo); + } + + private void prepareData(String value, List analysisCellList, + List collectionAnalysisCellList, int rowIndex, int columnIndex) { + if (StringUtils.isEmpty(value)) { + return; + } + AnalysisCell analysisCell = null; + int startIndex = 0; + int length = value.length(); + int lastPrepareDataIndex = 0; + out: + while (startIndex < length) { + int prefixIndex = value.indexOf(FILL_PREFIX, startIndex); + if (prefixIndex < 0) { + break out; + } + if (prefixIndex != 0) { + char prefixPrefixChar = value.charAt(prefixIndex - 1); + if (prefixPrefixChar == IGNORE_CHAR) { + startIndex = prefixIndex + 1; + continue; + } + } + int suffixIndex = -1; + while (suffixIndex == -1 && startIndex < length) { + suffixIndex = value.indexOf(FILL_SUFFIX, startIndex + 1); + if (suffixIndex < 0) { + break out; + } + startIndex = suffixIndex + 1; + char prefixSuffixChar = value.charAt(suffixIndex - 1); + if (prefixSuffixChar == IGNORE_CHAR) { + suffixIndex = -1; + } + } + if (analysisCell == null) { + analysisCell = new AnalysisCell(); + analysisCell.setRowIndex(rowIndex); + analysisCell.setColumnIndex(columnIndex); + analysisCell.setOnlyOneVariable(Boolean.TRUE); + List variableList = new ArrayList(); + analysisCell.setVariableList(variableList); + List prepareDataList = new ArrayList(); + analysisCell.setPrepareDataList(prepareDataList); + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); + } + String variable = value.substring(prefixIndex + 1, suffixIndex); + if (StringUtils.isEmpty(variable)) { + continue; + } + if (variable.startsWith(COLLECTION_PREFIX)) { + variable = variable.substring(1); + if (StringUtils.isEmpty(variable)) { + continue; + } + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION); + } + analysisCell.getVariableList().add(variable); + if (lastPrepareDataIndex == prefixIndex) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList() + .add(convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + lastPrepareDataIndex = suffixIndex + 1; + } + if (analysisCell != null) { + if (lastPrepareDataIndex == length) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + analysisCellList.add(analysisCell); + } else { + collectionAnalysisCellList.add(analysisCell); + } + } + } + + private String convertPrepareData(String prepareData) { + prepareData = prepareData.replaceAll(ESCAPE_FILL_PREFIX, FILL_PREFIX); + prepareData = prepareData.replaceAll(ESCAPE_FILL_SUFFIX, FILL_SUFFIX); + return prepareData; + } + +} diff --git a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java index 9e8af22..4d98138 100644 --- a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.handler; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -16,32 +18,56 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface CellWriteHandler extends WriteHandler { /** - * called before create the cell + * Called before create the cell * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param head + * Nullable.It is null in the case of fill data and without head. + * @param columnIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, - int relativeRowIndex, boolean isHead); + Integer columnIndex, Integer relativeRowIndex, Boolean isHead); + + /** + * Called after the cell is created + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param cell + * @param head + * Nullable.It is null in the case of fill data and without head. + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * It will always be false when fill data. + */ + void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); /** - * called after the cell is created + * Called after all operations on the cell have been completed * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param cell * @param head - * @param cellData - * Nullable. + * Nullable.It is null in the case of fill data and without head. + * @param cellDataList + * Nullable.It is null in the case of add header.There may be several when fill the data. * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * It will always be false when fill data. */ - void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead); + void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java index 1630bd3..29ac10b 100644 --- a/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java @@ -13,28 +13,47 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public interface RowWriteHandler extends WriteHandler { /** - * called before create the row + * Called before create the row * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param rowIndex * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ - void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead); + void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead); /** - * called after the row is created + * Called after the row is created * * @param writeSheetHolder * @param writeTableHolder - * Nullable + * Nullable.It is null without using table writes. * @param row * @param relativeRowIndex + * Nullable.It is null in the case of fill data. * @param isHead + * Nullable.It is null in the case of fill data. */ void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead); + Integer relativeRowIndex, Boolean isHead); + + /** + * Called after all operations on the row have been completed.This method is not called when fill the data. + * + * @param writeSheetHolder + * @param writeTableHolder + * Nullable.It is null without using table writes. + * @param row + * @param relativeRowIndex + * Nullable.It is null in the case of fill data. + * @param isHead + * Nullable.It is null in the case of fill data. + */ + void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java index 080abbf..b97e1d1 100644 --- a/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java @@ -11,7 +11,7 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface SheetWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the sheet * * @param writeWorkbookHolder * @param writeSheetHolder @@ -19,7 +19,7 @@ public interface SheetWriteHandler extends WriteHandler { void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder); /** - * called after the sheet is created + * Called after the sheet is created * * @param writeWorkbookHolder * @param writeSheetHolder diff --git a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java index 508c9c5..8dba964 100644 --- a/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java @@ -10,14 +10,21 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; public interface WorkbookWriteHandler extends WriteHandler { /** - * called before create the sheet + * Called before create the workbook */ void beforeWorkbookCreate(); /** - * called after the sheet is created + * Called after the workbook is created * * @param writeWorkbookHolder */ void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder); + + /** + * Called after all operations on the workbook have been completed + * + * @param writeWorkbookHolder + */ + void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder); } diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java index 7b95f1c..13bdac3 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.merge; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; @@ -18,13 +20,17 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; public abstract class AbstractMergeStrategy implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) {} + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { if (isHead) { return; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java index 4613891..791e264 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -2,6 +2,8 @@ package com.alibaba.excel.write.metadata.fill; import java.util.List; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; + /** * Read the cells of the template while populating the data. * @@ -11,7 +13,9 @@ public class AnalysisCell { private int columnIndex; private int rowIndex; private List variableList; - private String prepareData; + private List prepareDataList; + private Boolean onlyOneVariable; + private WriteTemplateAnalysisCellTypeEnum cellType; public int getColumnIndex() { return columnIndex; @@ -37,11 +41,49 @@ public class AnalysisCell { this.variableList = variableList; } - public String getPrepareData() { - return prepareData; + public List getPrepareDataList() { + return prepareDataList; + } + + public void setPrepareDataList(List prepareDataList) { + this.prepareDataList = prepareDataList; + } + + public Boolean getOnlyOneVariable() { + return onlyOneVariable; + } + + public void setOnlyOneVariable(Boolean onlyOneVariable) { + this.onlyOneVariable = onlyOneVariable; + } + + public WriteTemplateAnalysisCellTypeEnum getCellType() { + return cellType; + } + + public void setCellType(WriteTemplateAnalysisCellTypeEnum cellType) { + this.cellType = cellType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnalysisCell that = (AnalysisCell)o; + if (columnIndex != that.columnIndex) { + return false; + } + return rowIndex == that.rowIndex; } - public void setPrepareData(String prepareData) { - this.prepareData = prepareData; + @Override + public int hashCode() { + int result = columnIndex; + result = 31 * result + rowIndex; + return result; } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java new file mode 100644 index 0000000..a5bbe91 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java @@ -0,0 +1,83 @@ +package com.alibaba.excel.write.metadata.fill; + +import com.alibaba.excel.enums.WriteDirectionEnum; + +/** + * Fill config + * + * @author Jiaju Zhuang + **/ +public class FillConfig { + private WriteDirectionEnum direction; + /** + * Create a new row each time you use the list parameter.The default create if necessary. + *

+ * Warnning:If you use forceNewRow set true, will not be able to use asynchronous write file, simply + * say the whole file will be stored in memory. + */ + private Boolean forceNewRow; + private boolean hasInit; + + public WriteDirectionEnum getDirection() { + return direction; + } + + public void setDirection(WriteDirectionEnum direction) { + this.direction = direction; + } + + public Boolean getForceNewRow() { + return forceNewRow; + } + + public void setForceNewRow(Boolean forceNewRow) { + this.forceNewRow = forceNewRow; + } + + public void init() { + if (hasInit) { + return; + } + if (direction == null) { + direction = WriteDirectionEnum.VERTICAL; + } + if (forceNewRow == null) { + forceNewRow = Boolean.FALSE; + } + hasInit = true; + } + + public static FillConfigBuilder builder() { + return new FillConfigBuilder(); + } + + public static class FillConfigBuilder { + private FillConfig fillConfig; + + FillConfigBuilder() { + this.fillConfig = new FillConfig(); + } + + public FillConfigBuilder direction(WriteDirectionEnum direction) { + fillConfig.setDirection(direction); + return this; + } + + public FillConfigBuilder forceNewRow(Boolean forceNewRow) { + fillConfig.setForceNewRow(forceNewRow); + return this; + } + + public FillConfig build() { + return build(true); + } + + public FillConfig build(boolean autoInit) { + if (autoInit) { + fillConfig.init(); + } + return fillConfig; + } + + } +} diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java index 208fb41..1fb19fb 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -3,12 +3,13 @@ package com.alibaba.excel.write.metadata.holder; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet; import com.alibaba.excel.enums.HolderEnum; -import com.alibaba.excel.enums.WriteLastRowType; -import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.enums.WriteLastRowTypeEnum; import com.alibaba.excel.write.metadata.WriteSheet; /** @@ -22,14 +23,22 @@ public class WriteSheetHolder extends AbstractWriteHolder { */ private WriteSheet writeSheet; /*** - * poi sheet + * Current poi Sheet.This is only for writing, and there may be no data in version 07 when template data needs to be + * read. + *

    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link SXSSFSheet}
  • + *
*/ private Sheet sheet; - /** - * When reading version 07 with the template, the sheet cannot read template data, so need to use - * xssfSheet to get it. + /*** + * Current poi Sheet.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link XSSFSheet}
  • + *
*/ - private XSSFSheet xssfSheet; + private Sheet cachedSheet; /*** * sheetNo */ @@ -53,7 +62,7 @@ public class WriteSheetHolder extends AbstractWriteHolder { * @param writeSheet * @param writeWorkbookHolder */ - private WriteLastRowType writeLastRowType; + private WriteLastRowTypeEnum writeLastRowTypeEnum; public WriteSheetHolder(WriteSheet writeSheet, WriteWorkbookHolder writeWorkbookHolder) { super(writeSheet, writeWorkbookHolder, writeWorkbookHolder.getWriteWorkbook().getConvertAllFiled()); @@ -66,10 +75,10 @@ public class WriteSheetHolder extends AbstractWriteHolder { } this.parentWriteWorkbookHolder = writeWorkbookHolder; this.hasBeenInitializedTable = new HashMap(); - if (writeWorkbookHolder.getTemplateInputStream() == null && writeWorkbookHolder.getTemplateFile() == null) { - writeLastRowType = WriteLastRowType.COMMON_EMPTY; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeLastRowTypeEnum = WriteLastRowTypeEnum.TEMPLATE_EMPTY; } else { - writeLastRowType = WriteLastRowType.TEMPLATE_EMPTY; + writeLastRowTypeEnum = WriteLastRowTypeEnum.COMMON_EMPTY; } } @@ -93,12 +102,12 @@ public class WriteSheetHolder extends AbstractWriteHolder { return sheetNo; } - public XSSFSheet getXssfSheet() { - return xssfSheet; + public Sheet getCachedSheet() { + return cachedSheet; } - public void setXssfSheet(XSSFSheet xssfSheet) { - this.xssfSheet = xssfSheet; + public void setCachedSheet(Sheet cachedSheet) { + this.cachedSheet = cachedSheet; } public void setSheetNo(Integer sheetNo) { @@ -129,12 +138,12 @@ public class WriteSheetHolder extends AbstractWriteHolder { this.hasBeenInitializedTable = hasBeenInitializedTable; } - public WriteLastRowType getWriteLastRowType() { - return writeLastRowType; + public WriteLastRowTypeEnum getWriteLastRowTypeEnum() { + return writeLastRowTypeEnum; } - public void setWriteLastRowType(WriteLastRowType writeLastRowType) { - this.writeLastRowType = writeLastRowType; + public void setWriteLastRowTypeEnum(WriteLastRowTypeEnum writeLastRowTypeEnum) { + this.writeLastRowTypeEnum = writeLastRowTypeEnum; } /** @@ -145,25 +154,16 @@ public class WriteSheetHolder extends AbstractWriteHolder { public int getNewRowIndexAndStartDoWrite() { // 'getLastRowNum' doesn't matter if it has one or zero,is's zero int newRowIndex = 0; - switch (writeLastRowType) { + switch (writeLastRowTypeEnum) { case TEMPLATE_EMPTY: - if (parentWriteWorkbookHolder.getExcelType() == ExcelTypeEnum.XLSX) { - if (parentWriteWorkbookHolder.getTemplateLastRowMap().containsKey(sheetNo)) { - newRowIndex = parentWriteWorkbookHolder.getTemplateLastRowMap().get(sheetNo); - } - } else { - newRowIndex = sheet.getLastRowNum(); - } - newRowIndex++; - break; case HAS_DATA: - newRowIndex = sheet.getLastRowNum(); + newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); newRowIndex++; break; default: break; } - writeLastRowType = WriteLastRowType.HAS_DATA; + writeLastRowTypeEnum = WriteLastRowTypeEnum.HAS_DATA; return newRowIndex; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 0120142..ec364fb 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -1,20 +1,28 @@ package com.alibaba.excel.write.metadata.holder; import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import com.alibaba.excel.enums.HolderEnum; import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.IoUtils; import com.alibaba.excel.write.metadata.WriteWorkbook; /** @@ -24,14 +32,22 @@ import com.alibaba.excel.write.metadata.WriteWorkbook; */ public class WriteWorkbookHolder extends AbstractWriteHolder { /*** - * poi Workbook + * Current poi Workbook.This is only for writing, and there may be no data in version 07 when template data needs to + * be read. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link SXSSFWorkbook}
  • + *
*/ private Workbook workbook; - /** - * When reading version 07 with the template, the workbook cannot get the specific line number, so it - * needs to get the specific line number. + /*** + * Current poi Workbook.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link XSSFWorkbook}
  • + *
*/ - private XSSFWorkbook xssfWorkbook; + private Workbook cachedWorkbook; /** * current param */ @@ -80,11 +96,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * prevent duplicate creation of sheet objects */ private Map hasBeenInitializedSheet; - /** - * When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we are using - * the template, so we cache it - */ - private Map templateLastRowMap; public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); @@ -99,19 +110,16 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { } else { this.outputStream = writeWorkbook.getOutputStream(); } - if (writeWorkbook.getTemplateInputStream() != null) { - if (writeWorkbook.getTemplateInputStream().markSupported()) { - this.templateInputStream = writeWorkbook.getTemplateInputStream(); - } else { - this.templateInputStream = new BufferedInputStream(writeWorkbook.getTemplateInputStream()); - } - } - this.templateFile = writeWorkbook.getTemplateFile(); if (writeWorkbook.getAutoCloseStream() == null) { this.autoCloseStream = Boolean.TRUE; } else { this.autoCloseStream = writeWorkbook.getAutoCloseStream(); } + try { + copyTemplate(); + } catch (IOException e) { + throw new ExcelGenerateException("Copy template failure.", e); + } if (writeWorkbook.getExcelType() == null) { if (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) { this.excelType = ExcelTypeEnum.XLS; @@ -127,7 +135,25 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream(); } this.hasBeenInitializedSheet = new HashMap(); - this.templateLastRowMap = new HashMap(8); + } + + private void copyTemplate() throws IOException { + if (writeWorkbook.getTemplateFile() == null && writeWorkbook.getTemplateInputStream() == null) { + return; + } + byte[] templateFileByte = null; + if (writeWorkbook.getTemplateFile() != null) { + templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); + } else if (writeWorkbook.getTemplateInputStream() == null) { + try { + templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream()); + } finally { + if (autoCloseStream) { + writeWorkbook.getTemplateInputStream().close(); + } + } + } + this.tempTemplateInputStream = new ByteArrayInputStream(templateFileByte); } public Workbook getWorkbook() { @@ -138,12 +164,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.workbook = workbook; } - public XSSFWorkbook getXssfWorkbook() { - return xssfWorkbook; + public Workbook getCachedWorkbook() { + return cachedWorkbook; } - public void setXssfWorkbook(XSSFWorkbook xssfWorkbook) { - this.xssfWorkbook = xssfWorkbook; + public void setCachedWorkbook(Workbook cachedWorkbook) { + this.cachedWorkbook = cachedWorkbook; } public Map getHasBeenInitializedSheet() { @@ -226,14 +252,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = mandatoryUseInputStream; } - public Map getTemplateLastRowMap() { - return templateLastRowMap; - } - - public void setTemplateLastRowMap(Map templateLastRowMap) { - this.templateLastRowMap = templateLastRowMap; - } - @Override public HolderEnum holderType() { return HolderEnum.WORKBOOK; diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java index 51b03d1..7aafc44 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; @@ -27,28 +29,23 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } @Override - public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - initCellStyle(writeWorkbookHolder.getWorkbook()); - hasInitialized = true; - } + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { - if (!hasInitialized) { - initCellStyle(writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook()); - hasInitialized = true; - } } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead == null || head == null) { + return; + } if (isHead) { setHeadCellStyle(cell, head, relativeRowIndex); } else { @@ -56,6 +53,17 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } } + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + initCellStyle(writeWorkbookHolder.getWorkbook()); + hasInitialized = true; + } + /** * Initialization cell style * @@ -70,7 +78,7 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex); /** * Sets the cell style of content @@ -79,6 +87,6 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She * @param head * @param relativeRowIndex */ - protected abstract void setContentCellStyle(Cell cell, Head head, int relativeRowIndex); + protected abstract void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex); } diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java index 083d9cf..09903a0 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java @@ -29,7 +29,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (headCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = headCellStyleCache.get(columnIndex); @@ -49,7 +49,7 @@ public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyl } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { int columnIndex = head.getColumnIndex(); if (contentCellStyleCache.containsKey(columnIndex)) { CellStyle cellStyle = contentCellStyleCache.get(columnIndex); diff --git a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java index da5e53b..295cd55 100644 --- a/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java @@ -51,7 +51,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setHeadCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (headCellStyle == null) { return; } @@ -59,7 +59,7 @@ public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { } @Override - protected void setContentCellStyle(Cell cell, Head head, int relativeRowIndex) { + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (contentCellStyleList == null || contentCellStyleList.isEmpty()) { return; } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java index 67e9733..07afcfc 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -24,27 +26,28 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} - } + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) {} @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { - setColumnWidth(writeSheetHolder, cellData, cell, head, relativeRowIndex, isHead); + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + setColumnWidth(writeSheetHolder, cellDataList, cell, head, relativeRowIndex, isHead); } /** * Sets the column width when head create * * @param writeSheetHolder - * @param cellData + * @param cellDataList * @param cell * @param head * @param relativeRowIndex * @param isHead */ - protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead); + protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java index da9b7da..9908144 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -1,5 +1,7 @@ package com.alibaba.excel.write.style.column; +import java.util.List; + import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; @@ -12,10 +14,12 @@ import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; * @author Jiaju Zhuang */ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && relativeRowIndex != 0) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = relativeRowIndex != null && (isHead || relativeRowIndex == 0); + if (!needSetWidth) { return; } Integer width = columnWidth(head); @@ -36,4 +40,5 @@ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColum * @return */ protected abstract Integer columnWidth(Head head); + } diff --git a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java index fb807f3..006927f 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -1,12 +1,14 @@ package com.alibaba.excel.write.style.column; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.poi.ss.usermodel.Cell; import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; /** @@ -24,9 +26,10 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty private static final Map> CACHE = new HashMap>(8); @Override - protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, - int relativeRowIndex, boolean isHead) { - if (!isHead && cellData == null) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); + if (!needSetWidth) { return; } Map maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo()); @@ -34,24 +37,25 @@ public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthSty maxColumnWidthMap = new HashMap(16); CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap); } - Integer columnWidth = dataLength(cellData, cell, isHead); + Integer columnWidth = dataLength(cellDataList, cell, isHead); if (columnWidth < 0) { return; } if (columnWidth > MAX_COLUMN_WIDTH) { columnWidth = MAX_COLUMN_WIDTH; } - Integer maxColumnWidth = maxColumnWidthMap.get(head.getColumnIndex()); + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); if (maxColumnWidth == null || columnWidth > maxColumnWidth) { - maxColumnWidthMap.put(head.getColumnIndex(), columnWidth); - writeSheetHolder.getSheet().setColumnWidth(head.getColumnIndex(), columnWidth * 256); + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); } } - private Integer dataLength(CellData cellData, Cell cell, boolean isHead) { + private Integer dataLength(List cellDataList, Cell cell, Boolean isHead) { if (isHead) { return cell.getStringCellValue().getBytes().length; } + CellData cellData = cellDataList.get(0); switch (cellData.getType()) { case STRING: return cellData.getStringValue().getBytes().length; diff --git a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java index 3a6a343..303c818 100644 --- a/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java @@ -20,14 +20,23 @@ public abstract class AbstractRowHeightStyleStrategy implements RowWriteHandler, } @Override - public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, int rowIndex, - int relativeRowIndex, boolean isHead) { + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { } @Override public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - int relativeRowIndex, boolean isHead) { + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead == null) { + return; + } if (isHead) { setHeadColumnHeight(row, relativeRowIndex); } else { diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java index 92e18a8..13dfa17 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -1,6 +1,8 @@ package com.alibaba.easyexcel.test.core.fill; +import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; import lombok.Data; @@ -10,6 +12,7 @@ import lombok.Data; @Data public class FillData { private String name; - @NumberFormat("0#") + @NumberFormat("#") + @ExcelProperty(converter = DoubleStringConverter.class) private double number; } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java index 06236ca..93a5457 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -2,7 +2,9 @@ package com.alibaba.easyexcel.test.core.fill; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.poi.ss.usermodel.BorderStyle; import org.apache.poi.ss.usermodel.FillPatternType; @@ -10,6 +12,7 @@ import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.HorizontalAlignment; import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; @@ -19,9 +22,13 @@ import com.alibaba.easyexcel.test.core.style.StyleData; import com.alibaba.easyexcel.test.core.style.StyleDataListener; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.merge.LoopMergeStrategy; import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; @@ -39,52 +46,112 @@ public class FillDataTest { private static File file07; private static File file03; private static File simpleTemplate07; + private static File simpleTemplate03; + private static File fileComplex07; + private static File complexFillTemplate07; + private static File fileComplex03; + private static File complexFillTemplate03; + private static File fileHorizontal07; + private static File horizontalFillTemplate07; + private static File fileHorizontal03; + private static File horizontalFillTemplate03; @BeforeClass public static void init() { file07 = TestFileUtil.createNewFile("fill07.xlsx"); file03 = TestFileUtil.createNewFile("fill03.xls"); simpleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "simple.xlsx"); + simpleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "simple.xls"); + fileComplex07 = TestFileUtil.createNewFile("fillComplex07.xlsx"); + complexFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "complex.xlsx"); + fileComplex03 = TestFileUtil.createNewFile("fillComplex03.xls"); + complexFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "complex.xls"); + fileHorizontal07 = TestFileUtil.createNewFile("fillHorizontal07.xlsx"); + horizontalFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xlsx"); + fileHorizontal03 = TestFileUtil.createNewFile("fillHorizontal03.xls"); + horizontalFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xls"); } @Test public void t01Fill07() { - fill(file07); + fill(file07, simpleTemplate07); } @Test public void t02Fill03() { - fill(file03); + fill(file03, simpleTemplate03); } - private void fill(File file) { + @Test + public void t03ComplexFill07() { + complexFill(fileComplex07, complexFillTemplate07); + } + + @Test + public void t04ComplexFill03() { + complexFill(fileComplex03, complexFillTemplate03); + } + + @Test + public void t05HorizontalFill07() { + horizontalFill(fileHorizontal07, horizontalFillTemplate07); + } + + @Test + public void t06HorizontalFill03() { + horizontalFill(fileHorizontal03, horizontalFillTemplate03); + } + + private void horizontalFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + + List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); + Assert.assertEquals(list.size(), 5L); + Map map0 = (Map)list.get(0); + Assert.assertEquals("张三", map0.get(2)); + } + + private void complexFill(File file, File template) { + ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + List list = EasyExcel.read(file).sheet().headRowNumber(3).doReadSync(); + Assert.assertEquals(list.size(), 21L); + Map map19 = (Map)list.get(19); + Assert.assertEquals("张三", map19.get(0)); + } + + private void fill(File file, File template) { FillData fillData = new FillData(); fillData.setName("张三"); fillData.setNumber(5.2); - EasyExcel.write(file).withTemplate(simpleTemplate07).sheet().doFill(fillData); + EasyExcel.write(file, FillData.class).withTemplate(template).sheet().doFill(fillData); } - private List data() { - List list = new ArrayList(); - StyleData data = new StyleData(); - data.setString("字符串0"); - data.setString1("字符串01"); - StyleData data1 = new StyleData(); - data1.setString("字符串1"); - data1.setString1("字符串11"); - list.add(data); - list.add(data1); - return list; - } - - private List data10() { - List list = new ArrayList(); + private List data() { + List list = new ArrayList(); for (int i = 0; i < 10; i++) { - StyleData data = new StyleData(); - data.setString("字符串0"); - data.setString1("字符串01"); - list.add(data); + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); } return list; } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java index b7ba56b..fa32433 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java @@ -1,13 +1,19 @@ package com.alibaba.easyexcel.test.core.large; import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.alibaba.easyexcel.test.demo.write.DemoData; import com.alibaba.easyexcel.test.util.TestFileUtil; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; /** * @@ -15,12 +21,67 @@ import com.alibaba.excel.EasyExcel; */ public class LargeDataTest { private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataTest.class); + private static File fileFill07; + private static File template07; + private int i = 0; + + @BeforeClass + public static void init() { + fileFill07 = TestFileUtil.createNewFile("largefill07.xlsx"); + template07 = TestFileUtil.readFile("large" + File.separator + "fill.xlsx"); + } @Test - public void read() { + public void t01Read() { long start = System.currentTimeMillis(); EasyExcel.read(TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx", LargeData.class, new LargeDataListener()).headRowNumber(2).sheet().doRead(); LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); } + + @Test + public void t02Fill() { + ExcelWriter excelWriter = EasyExcel.write(fileFill07).withTemplate(template07).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 100; j++) { + excelWriter.fill(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + int size = i + 5000; + for (; i < size; i++) { + LargeData largeData = new LargeData(); + list.add(largeData); + largeData.setStr1("str1-" + i); + largeData.setStr2("str2-" + i); + largeData.setStr3("str3-" + i); + largeData.setStr4("str4-" + i); + largeData.setStr5("str5-" + i); + largeData.setStr6("str6-" + i); + largeData.setStr7("str7-" + i); + largeData.setStr8("str8-" + i); + largeData.setStr9("str9-" + i); + largeData.setStr10("str10-" + i); + largeData.setStr11("str11-" + i); + largeData.setStr12("str12-" + i); + largeData.setStr13("str13-" + i); + largeData.setStr14("str14-" + i); + largeData.setStr15("str15-" + i); + largeData.setStr16("str16-" + i); + largeData.setStr17("str17-" + i); + largeData.setStr18("str18-" + i); + largeData.setStr19("str19-" + i); + largeData.setStr20("str20-" + i); + largeData.setStr21("str21-" + i); + largeData.setStr22("str22-" + i); + largeData.setStr23("str23-" + i); + largeData.setStr24("str24-" + i); + largeData.setStr25("str25-" + i); + } + return list; + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java new file mode 100644 index 0000000..8890b82 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java @@ -0,0 +1,12 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class FillData { + private String name; + private double number; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java new file mode 100644 index 0000000..0159cbc --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -0,0 +1,181 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Ignore; +import org.junit.Test; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +/** + * 写的填充写法 + * + * @author Jiaju Zhuang + */ +@Ignore +public class FillTest { + /** + * 最简单的填充 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + // 方案2 根据Map填充 + fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + Map map = new HashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } + + /** + * 填充列表 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + // 千万别忘记关闭流 + excelWriter.finish(); + } + + /** + * 复杂的填充 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + + /** + * 横向的填充 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java index bff544a..b92d44a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java @@ -1,5 +1,7 @@ package com.alibaba.easyexcel.test.demo.write; +import java.util.List; + import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CreationHelper; @@ -25,13 +27,19 @@ public class CustomCellWriteHandler implements CellWriteHandler { @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, int relativeRowIndex, boolean isHead) { + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, - Cell cell, Head head, int relativeRowIndex, boolean isHead) { + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { // 这里可以对cell进行任何操作 LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); if (isHead && cell.getColumnIndex() == 0) { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java index 1424c42..43726ad 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java @@ -1,11 +1,13 @@ package com.alibaba.easyexcel.test.temp.large; +import lombok.Builder; import lombok.Data; /** * @author Jiaju Zhuang */ @Data +@Builder public class LargeData { private String str1; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 7828bd0..0a19c35 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -4,7 +4,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFRow; @@ -72,29 +75,101 @@ public class PoiTest { xssfWorkbook.close(); } + @Test + public void lastRowNum255() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "complex.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + xssfSheet.shiftRows(2, 4, 10); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void cp() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); +// Row r2= xssfSheet.createRow(2); +// r2.createCell(1); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + + + SXSSFSheet sxssfSheet = sxssfWorkbook.getSheetAt(0); + sxssfSheet.createRow(2); + + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum233443() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + XSSFRow row = xssfSheet.getRow(2); + xssfSheet.removeRow(row); + new CellCopyPolicy().createBuilder().build(); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2333() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + @Test public void testread() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); -// -// Cell cell = xssfSheet.getRow(0).createCell(9); - + // + // Cell cell = xssfSheet.getRow(0).createCell(9); String file1 = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; SXSSFWorkbook xssfWorkbook1 = new SXSSFWorkbook(new XSSFWorkbook(file1)); Sheet xssfSheet1 = xssfWorkbook1.getXSSFWorkbook().getSheetAt(0); -// Cell cell1 = xssfSheet1.getRow(0).createCell(9); - + // Cell cell1 = xssfSheet1.getRow(0).createCell(9); xssfWorkbook.close(); xssfWorkbook1.close(); } - @Test public void testreadRead() throws IOException { String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; 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 02e38c0..5c1620e 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 @@ -5,6 +5,7 @@ import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.regex.Pattern; import org.apache.poi.ss.formula.functions.T; import org.apache.poi.xssf.streaming.SXSSFCell; @@ -44,6 +45,31 @@ public class PoiWriteTest { sxxsFWorkbook.write(fileOutputStream); } + private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$"); + + @Test + public void part() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${number}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("胜多负少").matches()); + } + + private static final Pattern FILL_PATTERN2 = Pattern.compile("测试"); + + @Test + public void part2() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试呀").find()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("测试u").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试").matches()); + + } + @Test public void test() throws Exception { Class clazz = TestCell.class; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java index ad07b70..2eb4d1a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -27,7 +27,7 @@ public class HgTest { @Test public void hh() throws IOException { List list = - EasyExcel.read(new FileInputStream("D:\\test\\MRP生产视图(1).xlsx")).headRowNumber(0).sheet().doReadSync(); + EasyExcel.read(new FileInputStream("D:\\test\\201909301017rule.xlsx")).headRowNumber(2).sheet().doReadSync(); for (Object data : list) { LOGGER.info("返回数据:{}", JSON.toJSONString(data)); } diff --git a/src/test/resources/demo/fill/complex.xlsx b/src/test/resources/demo/fill/complex.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..537671369ce0724b61b2654322897087d55879ac GIT binary patch literal 10297 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/complexFillWithTable.xlsx b/src/test/resources/demo/fill/complexFillWithTable.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4de1a1e791e0835dd6fb91a3723f441f479b3665 GIT binary patch literal 10290 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/horizontal.xlsx b/src/test/resources/demo/fill/horizontal.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c8b4564eeab4fc95ffca5f3c78cfb9a86254f28e GIT binary patch literal 10307 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/demo/fill/simple.xlsx b/src/test/resources/demo/fill/simple.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..7514d1dabf0766b7bfad8accca1878c176536c2d GIT binary patch literal 10135 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 AegFUf literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/complex.xls b/src/test/resources/fill/complex.xls new file mode 100644 index 0000000000000000000000000000000000000000..d5758951c6e0838446ddd6f1ba70428b9a9c3e0a GIT binary patch literal 19456 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 AcmMzZ literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/complex.xlsx b/src/test/resources/fill/complex.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..537671369ce0724b61b2654322897087d55879ac GIT binary patch literal 10297 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 literal 0 HcmV?d00001 diff --git a/src/test/resources/fill/horizontal.xls b/src/test/resources/fill/horizontal.xls new file mode 100644 index 0000000000000000000000000000000000000000..570f9012d725554874cbadae3fc45996d97f41b6 GIT binary patch literal 19968 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 K;2pC4o%esF<}~sE delta 4411 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 diff --git a/src/test/resources/large/fill.xlsx b/src/test/resources/large/fill.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c3c376d31a6ba4920046118683ed0593934ca5de GIT binary patch literal 11475 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 literal 0 HcmV?d00001 From 2fb107577b15f6900c2a84b22a12e229c3cec693 Mon Sep 17 00:00:00 2001 From: huaiyu Date: Wed, 25 Sep 2019 13:27:02 +0800 Subject: [PATCH 09/17] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=86=99=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=8A=A0=E5=AF=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alibaba/excel/ExcelWriter.java | 8 +++ .../alibaba/excel/context/WriteContext.java | 13 +++++ .../excel/context/WriteContextImpl.java | 49 +++++++++++++++++++ .../alibaba/excel/write/ExcelBuilderImpl.java | 5 ++ .../builder/ExcelWriterSheetBuilder.java | 14 ++++++ .../metadata/holder/WriteWorkbookHolder.java | 5 ++ .../easyexcel/test/demo/write/WriteTest.java | 2 + 7 files changed, 96 insertions(+) diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index 65a99fe..3a2dde5 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -331,4 +331,12 @@ public class ExcelWriter { public WriteContext writeContext() { return excelBuilder.writeContext(); } + + /** + * doEncrypt + * @param password + */ + public void doEncrypt(String password){ + excelBuilder.setPassword(password); + } } diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java index e561319..a5e6d58 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ b/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -69,6 +69,19 @@ public interface WriteContext { */ void finish(); + /** + * isEncrypt + * @return + */ + boolean isEncrypt(); + + /** + * setPassword + * @param password + * @return + */ + void setPassword(String password); + /** * Current sheet * diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index c71d08d..8972424 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -1,8 +1,16 @@ package com.alibaba.excel.context; +import com.alibaba.excel.util.StringUtils; +import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Map; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; @@ -243,6 +251,12 @@ public class WriteContextImpl implements WriteContext { } try { writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); + if (isEncrypt()){ + LOGGER.debug("start encrypt"); + encrypt(writeWorkbookHolder); + LOGGER.debug("finish encrypt"); + + } writeWorkbookHolder.getWorkbook().close(); } catch (Throwable t) { throwCanNotCloseIo(t); @@ -274,6 +288,16 @@ public class WriteContextImpl implements WriteContext { } } + @Override + public boolean isEncrypt() { + return !StringUtils.isEmpty(writeWorkbookHolder.getPassword()); + } + + @Override + public void setPassword(String password) { + writeWorkbookHolder.setPassword(password); + } + private void throwCanNotCloseIo(Throwable t) { throw new ExcelGenerateException("Can not close IO", t); } @@ -297,4 +321,29 @@ public class WriteContextImpl implements WriteContext { public Workbook getWorkbook() { return writeWorkbookHolder.getWorkbook(); } + + /** + * do encrypt + * @param writeWorkbookHolder + */ + private void encrypt(WriteWorkbookHolder writeWorkbookHolder){ + try { + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard); + + Encryptor enc = info.getEncryptor(); + enc.confirmPassword(writeWorkbookHolder.getPassword()); + + OPCPackage opc = OPCPackage.open(writeWorkbookHolder.getFile(), PackageAccess.READ_WRITE); + OutputStream os = enc.getDataStream(fs); + opc.save(os); + opc.close(); + //write the encrypted file back to the stream + FileOutputStream fos = new FileOutputStream(writeWorkbookHolder.getFile()); + fs.writeFilesystem(fos); + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 3d8067a..27f6590 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -88,6 +88,11 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } + @Override + public void setPassword(String password) { + context.setPassword(password); + } + @Override public void finish() { if (context != null) { 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 8a9e0c6..893d218 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -148,6 +148,20 @@ public class ExcelWriterSheetBuilder { excelWriter.finish(); } + /** + * write with password + * @param data + * @param password + */ + public void doWrite(List data,String password) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); + } + excelWriter.write(data, build()); + excelWriter.doEncrypt(password); + excelWriter.finish(); + } + public ExcelWriterTableBuilder table() { return table(null); } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index ec364fb..931a450 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -97,6 +97,11 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { */ private Map hasBeenInitializedSheet; + /** + * password + */ + private String password; + public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); this.writeWorkbook = writeWorkbook; diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 76c3c71..a86f4e5 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -58,6 +58,8 @@ public class WriteTest { ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); excelWriter.write(data(), writeSheet); + // doEncrypt 进行加密 + //excelWriter.doEncrypt("123456"); /// 千万别忘记finish 会帮忙关闭流 excelWriter.finish(); } From 3b2c91594e2d2a2e41113d9a079d0f5d9ea74b30 Mon Sep 17 00:00:00 2001 From: huaiyu Date: Thu, 10 Oct 2019 15:11:36 +0800 Subject: [PATCH 10/17] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8E=9F=E6=9D=A5?= =?UTF-8?q?=E7=9A=84=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alibaba/excel/ExcelWriter.java | 20 ++++++++++-------- .../com/alibaba/excel/write/ExcelBuilder.java | 9 ++++++++ .../alibaba/excel/write/ExcelBuilderImpl.java | 21 ++++++++++++++----- .../builder/ExcelWriterSheetBuilder.java | 3 +-- .../easyexcel/test/demo/write/WriteTest.java | 5 ++--- 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index 3a2dde5..4800f84 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -138,7 +138,17 @@ public class ExcelWriter { * @return this current writer */ public ExcelWriter write(List data, WriteSheet writeSheet) { - return write(data, writeSheet, null); + return write(data, writeSheet, (WriteTable)null); + } + + + public ExcelWriter write(List data, WriteSheet writeSheet, String password) { + return write(data, writeSheet, null, password); + } + + private ExcelWriter write(List data, WriteSheet writeSheet, WriteTable writeTable, String password) { + excelBuilder.addContent(data, writeSheet, writeTable, password); + return this; } /** @@ -331,12 +341,4 @@ public class ExcelWriter { public WriteContext writeContext() { return excelBuilder.writeContext(); } - - /** - * doEncrypt - * @param password - */ - public void doEncrypt(String password){ - excelBuilder.setPassword(password); - } } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java index 8a60444..8a7d57d 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -73,4 +73,13 @@ public interface ExcelBuilder { * Close io */ void finish(); + + /** + * add password + * @param data + * @param writeSheet + * @param writeTable + * @param password + */ + void addContent(List data, WriteSheet writeSheet, WriteTable writeTable, String password); } diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 27f6590..1928447 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -88,11 +88,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - @Override - public void setPassword(String password) { - context.setPassword(password); - } - @Override public void finish() { if (context != null) { @@ -100,6 +95,22 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } + @Override + public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable, String password) { + try { + context.currentSheet(writeSheet); + context.currentTable(writeTable); + context.setPassword(password); + doAddContent(data); + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelGenerateException(e); + } + } + @Override public void merge(int firstRow, int lastRow, int firstCol, int lastCol) { CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); 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 893d218..194fedb 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -157,8 +157,7 @@ public class ExcelWriterSheetBuilder { if (excelWriter == null) { throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); } - excelWriter.write(data, build()); - excelWriter.doEncrypt(password); + excelWriter.write(data, build(), password); excelWriter.finish(); } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index a86f4e5..b2a1722 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -57,9 +57,8 @@ public class WriteTest { // 这里 需要指定写用哪个class去读 ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - excelWriter.write(data(), writeSheet); - // doEncrypt 进行加密 - //excelWriter.doEncrypt("123456"); + excelWriter.write(data(), writeSheet,"12345"); + // 进行加密 /// 千万别忘记finish 会帮忙关闭流 excelWriter.finish(); } From 4f9bcc70739ec0f4190841ecd751429e15b18881 Mon Sep 17 00:00:00 2001 From: huaiyu Date: Thu, 10 Oct 2019 15:20:26 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=86=B2=E7=AA=81=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8E=9F=E6=9D=A5=E7=9A=84=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/alibaba/excel/write/ExcelBuilderImpl.java | 10 ++++++++-- .../write/metadata/holder/WriteWorkbookHolder.java | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index 1928447..c1a00c2 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -98,10 +98,16 @@ public class ExcelBuilderImpl implements ExcelBuilder { @Override public void addContent(List data, WriteSheet writeSheet, WriteTable writeTable, String password) { try { - context.currentSheet(writeSheet); + if (data == null) { + return; + } + context.currentSheet(writeSheet, WriteTypeEnum.ADD); context.currentTable(writeTable); context.setPassword(password); - doAddContent(data); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); + } + excelWriteAddExecutor.add(data); } catch (RuntimeException e) { finish(); throw e; diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 931a450..65559db 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -261,4 +261,12 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { public HolderEnum holderType() { return HolderEnum.WORKBOOK; } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } } From 8a2cb69be90511a56db8b4916480ef14ac52b2a5 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Fri, 11 Oct 2019 18:32:09 +0800 Subject: [PATCH 12/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=85=A8=E9=83=A8sheet=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + quickstart.md | 33 ++++++-- .../excel/analysis/v03/XlsSaxAnalyser.java | 1 - .../exception/ExcelDataConvertException.java | 57 +++++++++++++ .../metadata/property/ExcelHeadProperty.java | 8 ++ .../listener/ModelBuildEventListener.java | 34 ++++++-- .../alibaba/excel/util/ConverterUtils.java | 22 +++-- .../write/builder/ExcelWriterBuilder.java | 11 +++ .../builder/ExcelWriterSheetBuilder.java | 11 +++ .../builder/ExcelWriterTableBuilder.java | 11 +++ .../handler/DefaultWriteHandlerLoader.java | 20 +++-- .../write/metadata/WriteBasicParameter.java | 12 +++ .../metadata/holder/AbstractWriteHolder.java | 25 +++++- .../test/core/annotation/AnnotationData.java | 2 + .../core/annotation/AnnotationDataTest.java | 1 + .../core/exception/ExceptionDataListener.java | 2 +- .../read/CellDataDemoHeadDataListener.java | 50 +++++++++++ .../test/demo/read/CellDataReadDemoData.java | 22 +++++ .../test/demo/read/DemoDataListener.java | 1 + .../test/demo/read/DemoExceptionListener.java | 80 ++++++++++++++++++ .../test/demo/read/DemoHeadDataListener.java | 8 +- .../test/demo/read/ExceptionDemoData.java | 18 ++++ .../easyexcel/test/demo/read/ReadTest.java | 22 ++++- .../easyexcel/test/demo/web/WebTest.java | 2 +- .../alibaba/easyexcel/test/temp/LockTest.java | 11 +++ .../easyexcel/test/temp/poi/PoiWriteTest.java | 39 +++++++-- src/test/resources/demo/cellDataDemo.xlsx | Bin 0 -> 10323 bytes update.md | 4 + 28 files changed, 464 insertions(+), 44 deletions(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java create mode 100644 src/test/resources/demo/cellDataDemo.xlsx diff --git a/README.md b/README.md index 583cc4b..bf9871a 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 728f373..ffb1f4b 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 7134e6f..129b74e 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 74630f0..4af2569 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 09d5560..4bffce6 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 6bea593..a368024 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 1a1e70a..b6244c2 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 5e699c3..3faad69 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 8a9e0c6..26acff5 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 14f755a..eca1fe0 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 f52af6b..dff84d7 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 3a43ff6..2689cd9 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 80d3e7b..72f5f9b 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 2a028f3..f1e9119 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 5acd5db..4489f1c 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 ffad838..d9c26e2 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 0000000..896cea6 --- /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 0000000..f5d01e0 --- /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 bf4dd63..434af3b 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 0000000..0f00719 --- /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 bc55052..184409a 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 0000000..8040728 --- /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 06ca4b6..3fb514c 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 8fff02c..cc52562 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 2e8931c..fb39de1 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 5c1620e..3a70d0d 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 0000000000000000000000000000000000000000..947229c70e166e5b65a9f19b27fe3af85cf4a026 GIT binary patch literal 10323 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 literal 0 HcmV?d00001 diff --git a/update.md b/update.md index b4dde63..04eec04 100644 --- a/update.md +++ b/update.md @@ -2,6 +2,10 @@ * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 +* 新增写支持 禁用头样式 autoHeadStyle +* 用map读取数据 空的单元格也会有个 null的数据 +* 转换报错 能获取到对应的行号和列号 +* 优化读取全部sheet方案 # 2.0.5 * 优化07版超大文件读取方案 From 44068c94f97c1a553900a05f57ec53ef45f6603f Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 14 Oct 2019 18:24:02 +0800 Subject: [PATCH 13/17] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E3=80=81=E5=AF=BC=E5=87=BA=E5=8A=A0=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- quickstart.md | 34 ++-- .../java/com/alibaba/excel/ExcelReader.java | 37 ++-- .../java/com/alibaba/excel/ExcelWriter.java | 12 +- .../alibaba/excel/analysis/ExcelAnalyser.java | 12 +- .../excel/analysis/ExcelAnalyserImpl.java | 39 ++++- .../excel/analysis/ExcelReadExecutor.java | 10 +- .../analysis/v03/XlsListSheetListener.java | 6 +- .../excel/analysis/v03/XlsSaxAnalyser.java | 14 +- .../v03/handlers/BofRecordHandler.java | 42 ++++- .../excel/analysis/v07/XlsxSaxAnalyser.java | 18 +- .../annotation/ExcelIgnoreUnannotated.java | 18 ++ .../excel/context/AnalysisContext.java | 4 +- .../excel/context/AnalysisContextImpl.java | 57 +----- .../alibaba/excel/context/WriteContext.java | 12 -- .../excel/context/WriteContextImpl.java | 162 ++++++++++++++---- .../excel/metadata/BasicParameter.java | 1 + .../metadata/property/ExcelHeadProperty.java | 8 +- .../read/builder/ExcelReaderBuilder.java | 18 ++ .../excel/read/metadata/ReadSheet.java | 13 ++ .../excel/read/metadata/ReadWorkbook.java | 12 ++ .../metadata/holder/ReadWorkbookHolder.java | 17 +- .../com/alibaba/excel/util/FileUtils.java | 9 + .../com/alibaba/excel/util/SheetUtils.java | 67 ++++++++ .../com/alibaba/excel/util/WorkBookUtil.java | 14 +- .../alibaba/excel/write/ExcelBuilderImpl.java | 1 - .../write/builder/ExcelWriterBuilder.java | 13 ++ .../builder/ExcelWriterSheetBuilder.java | 13 -- .../excel/write/metadata/WriteWorkbook.java | 15 ++ .../metadata/holder/WriteWorkbookHolder.java | 17 +- .../test/core/encrypt/EncryptData.java | 14 ++ .../core/encrypt/EncryptDataListener.java | 35 ++++ .../test/core/encrypt/EncryptDataTest.java | 88 ++++++++++ .../MultipleSheetsDataTest.java | 14 ++ .../MultipleSheetsListener.java | 1 + .../easyexcel/test/demo/read/ReadTest.java | 27 ++- .../easyexcel/test/demo/write/DemoData.java | 7 + .../easyexcel/test/demo/write/WriteTest.java | 2 +- .../easyexcel/test/temp/poi/Poi3Test.java | 73 ++++++++ .../easyexcel/test/temp/poi/PoiTest.java | 2 + .../easyexcel/test/temp/simple/Wirte.java | 2 +- update.md | 5 +- 41 files changed, 741 insertions(+), 224 deletions(-) create mode 100644 src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java create mode 100644 src/main/java/com/alibaba/excel/util/SheetUtils.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java create mode 100644 src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java diff --git a/quickstart.md b/quickstart.md index ffb1f4b..c9fdd7d 100644 --- a/quickstart.md +++ b/quickstart.md @@ -21,7 +21,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java) * [最简单的读](#simpleRead) * [指定列的下标或者列名](#indexOrNameRead) -* [读多个sheet](#repeatedRead) +* [读多个或者全部sheet](#repeatedRead) * [日期、数字或者自定义格式转换](#converterRead) * [多行头](#complexHeaderRead) * [同步的返回](#synchronousRead) @@ -180,7 +180,7 @@ public class IndexOrNameData { ##### 代码 ```java /** - * 读多个sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 + * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 *

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

@@ -190,24 +190,21 @@ public class IndexOrNameData { */ @Test public void repeatedRead() { - // 方法1 如果 sheet1 sheet2 都是同一数据 监听器和头 都写到最外层 String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet1 = EasyExcel.readSheet(0).build(); - ReadSheet readSheet2 = EasyExcel.readSheet(1).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); + // 读取全部sheet + // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); - // 方法2 如果 sheet1 sheet2 数据不一致的话 + // 读取部分sheet fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - excelReader = EasyExcel.read(fileName).build(); + ExcelReader excelReader = EasyExcel.read(fileName).build(); // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - readSheet1 = EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - readSheet2 = EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); + ReadSheet readSheet1 = + EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + ReadSheet readSheet2 = + EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能 + excelReader.read(readSheet1, readSheet2); // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 excelReader.finish(); } @@ -518,6 +515,11 @@ public class DemoData { private Date date; @ExcelProperty("数字标题") private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; } ``` ##### 代码 diff --git a/src/main/java/com/alibaba/excel/ExcelReader.java b/src/main/java/com/alibaba/excel/ExcelReader.java index 808ee56..46f2587 100644 --- a/src/main/java/com/alibaba/excel/ExcelReader.java +++ b/src/main/java/com/alibaba/excel/ExcelReader.java @@ -2,6 +2,7 @@ package com.alibaba.excel; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.slf4j.Logger; @@ -147,27 +148,41 @@ public class ExcelReader { /** * Parse all sheet content by default + * + * @deprecated lease use {@link #readAll()} */ + @Deprecated public void read() { - ExcelReadExecutor excelReadExecutor = excelAnalyser.excelExecutor(); - if (excelReadExecutor.sheetList().isEmpty()) { - LOGGER.warn("Excel doesn't have any sheets."); - return; - } - for (ReadSheet readSheet : excelReadExecutor.sheetList()) { - read(readSheet); - } + readAll(); + } + + /*** + * Parse all sheet content by default + */ + public void readAll() { + checkFinished(); + excelAnalyser.analysis(null, Boolean.TRUE); } /** - * Parse the specified sheet,SheetNo start from 1 + * Parse the specified sheet,SheetNo start from 0 * * @param readSheet * Read sheet */ - public ExcelReader read(ReadSheet readSheet) { + public ExcelReader read(ReadSheet... readSheet) { + return read(Arrays.asList(readSheet)); + } + + /** + * Read multiple sheets. + * + * @param readSheetList + * @return + */ + public ExcelReader read(List readSheetList) { checkFinished(); - excelAnalyser.analysis(readSheet); + excelAnalyser.analysis(readSheetList, Boolean.FALSE); return this; } diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java index 4800f84..65a99fe 100644 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ b/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -138,17 +138,7 @@ public class ExcelWriter { * @return this current writer */ public ExcelWriter write(List data, WriteSheet writeSheet) { - return write(data, writeSheet, (WriteTable)null); - } - - - public ExcelWriter write(List data, WriteSheet writeSheet, String password) { - return write(data, writeSheet, null, password); - } - - private ExcelWriter write(List data, WriteSheet writeSheet, WriteTable writeTable, String password) { - excelBuilder.addContent(data, writeSheet, writeTable, password); - return this; + return write(data, writeSheet, null); } /** diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java index 6d014bb..83de6ac 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java @@ -1,5 +1,7 @@ package com.alibaba.excel.analysis; +import java.util.List; + import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.metadata.ReadSheet; @@ -10,12 +12,14 @@ import com.alibaba.excel.read.metadata.ReadSheet; */ public interface ExcelAnalyser { /** - * parse one sheet + * parse the sheet * - * @param readSheet - * sheet to read + * @param readSheetList + * Which sheets you need to read. + * @param readAll + * The readSheetList parameter is ignored, and all sheets are read. */ - void analysis(ReadSheet readSheet); + void analysis(List readSheetList, Boolean readAll); /** * Complete the entire read file.Release the cache and close stream diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index a924b21..3b4754d 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -1,8 +1,13 @@ package com.alibaba.excel.analysis; +import java.io.FileInputStream; import java.io.InputStream; +import java.util.List; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.crypt.Decryptor; +import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.util.IOUtils; @@ -19,7 +24,9 @@ import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.ReadWorkbook; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.StringUtils; /** * @author jipengfei @@ -64,7 +71,8 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { InputStream decryptedStream = null; try { decryptedStream = - DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), null); + DocumentFactoryHelper.getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), + analysisContext.readWorkbookHolder().getPassword()); excelReadExecutor = new XlsxSaxAnalyser(analysisContext, decryptedStream); return; } finally { @@ -74,6 +82,9 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { poifsFileSystem.close(); } } + if (analysisContext.readWorkbookHolder().getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(analysisContext.readWorkbookHolder().getPassword()); + } excelReadExecutor = new XlsSaxAnalyser(analysisContext, poifsFileSystem); break; case XLSX: @@ -84,17 +95,24 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } @Override - public void analysis(ReadSheet readSheet) { + public void analysis(List readSheetList, Boolean readAll) { try { - analysisContext.currentSheet(excelReadExecutor, readSheet); + if (!readAll && CollectionUtils.isEmpty(readSheetList)) { + throw new IllegalArgumentException("Specify at least one read sheet."); + } try { - excelReadExecutor.execute(); + excelReadExecutor.execute(readSheetList, readAll); } catch (ExcelAnalysisStopException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Custom stop!"); } } - analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + // The last sheet is read + if (excelReadExecutor instanceof XlsSaxAnalyser) { + if (analysisContext.readSheetHolder() != null) { + analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + } + } } catch (RuntimeException e) { finish(); throw e; @@ -110,6 +128,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { return; } ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + try { if (readWorkbookHolder.getReadCache() != null) { readWorkbookHolder.getReadCache().destroy(); @@ -146,6 +165,16 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } catch (Throwable t) { throwCanNotCloseIo(t); } + + clearEncrypt03(); + } + + private void clearEncrypt03() { + if (StringUtils.isEmpty(analysisContext.readWorkbookHolder().getPassword()) + || !ExcelTypeEnum.XLS.equals(analysisContext.readWorkbookHolder().getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); } private void throwCanNotCloseIo(Throwable t) { diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java index 515eeb9..10ced98 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -19,8 +19,12 @@ public interface ExcelReadExecutor { List sheetList(); /** - * Read sheet + * Read the sheet. + * + * @param readSheetList + * Which sheets you need to read. + * @param readAll + * The readSheetList parameter is ignored, and all sheets are read. */ - void execute(); - + void execute(List readSheetList, Boolean readAll); } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java index 62d5517..6f8e716 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java @@ -10,7 +10,6 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; import org.apache.poi.hssf.eventusermodel.HSSFListener; import org.apache.poi.hssf.eventusermodel.HSSFRequest; import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; -import org.apache.poi.hssf.record.BoundSheetRecord; import org.apache.poi.hssf.record.Record; import org.apache.poi.poifs.filesystem.POIFSFileSystem; @@ -27,13 +26,14 @@ import com.alibaba.excel.read.metadata.ReadSheet; public class XlsListSheetListener implements HSSFListener { private POIFSFileSystem poifsFileSystem; private List sheetList; - private XlsRecordHandler bofRecordHandler; + private BofRecordHandler bofRecordHandler; public XlsListSheetListener(AnalysisContext analysisContext, POIFSFileSystem poifsFileSystem) { this.poifsFileSystem = poifsFileSystem; sheetList = new ArrayList(); - bofRecordHandler = new BofRecordHandler(analysisContext, sheetList, false); + bofRecordHandler = new BofRecordHandler(analysisContext, sheetList, false, false); bofRecordHandler.init(); + bofRecordHandler.init(null, true); } @Override 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 129b74e..21977a4 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -60,6 +60,8 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); private POIFSFileSystem poifsFileSystem; + private Boolean readAll; + private List readSheetList; private int lastRowNumber; private int lastColumnNumber; /** @@ -91,7 +93,9 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { } @Override - public void execute() { + public void execute(List readSheetList, Boolean readAll) { + this.readAll = readAll; + this.readSheetList = readSheetList; MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); formatListener = new FormatTrackingHSSFListener(listener); workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); @@ -195,9 +199,9 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { // The table has been counted and there are no duplicate statistics if (sheets == null) { sheets = new ArrayList(); - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, false)); + recordHandlers.add(new BofRecordHandler(analysisContext, sheets, false, true)); } else { - recordHandlers.add(new BofRecordHandler(analysisContext, sheets, true)); + recordHandlers.add(new BofRecordHandler(analysisContext, sheets, true, true)); } recordHandlers.add(new FormulaRecordHandler(stubWorkbook, formatListener)); recordHandlers.add(new LabelRecordHandler()); @@ -211,6 +215,10 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { for (XlsRecordHandler x : recordHandlers) { x.init(); + if (x instanceof BofRecordHandler) { + BofRecordHandler bofRecordHandler = (BofRecordHandler)x; + bofRecordHandler.init(readSheetList, readAll); + } } } } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java index d6a93a5..3a86294 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java +++ b/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java @@ -10,6 +10,7 @@ import org.apache.poi.hssf.record.Record; import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.util.SheetUtils; /** * Record handler @@ -21,13 +22,18 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { private BoundSheetRecord[] orderedBsrs; private int sheetIndex; private List sheets; + private Boolean readAll; + private List readSheetList; private AnalysisContext context; private boolean alreadyInit; + private boolean needInitSheet; - public BofRecordHandler(AnalysisContext context, List sheets, boolean alreadyInit) { + public BofRecordHandler(AnalysisContext context, List sheets, boolean alreadyInit, + boolean needInitSheet) { this.context = context; this.sheets = sheets; this.alreadyInit = alreadyInit; + this.needInitSheet = needInitSheet; } @Override @@ -45,15 +51,34 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { if (orderedBsrs == null) { orderedBsrs = BoundSheetRecord.orderByBofPosition(boundSheetRecords); } + String sheetName = orderedBsrs[sheetIndex].getSheetname(); + // Find the currently read sheet + ReadSheet readSheet = null; if (!alreadyInit) { - ReadSheet readSheet = new ReadSheet(sheetIndex, orderedBsrs[sheetIndex].getSheetname()); + readSheet = new ReadSheet(sheetIndex, sheetName); sheets.add(readSheet); } - if (context.readSheetHolder() != null) { - if (sheetIndex == context.readSheetHolder().getSheetNo()) { + if (needInitSheet) { + if (readSheet == null) { + for (ReadSheet sheet : sheets) { + if (sheet.getSheetNo() == sheetIndex) { + readSheet = sheet; + break; + } + } + } + assert readSheet != null : "Can't find the sheet."; + context.readWorkbookHolder().setIgnoreRecord03(Boolean.TRUE); + // Copy the parameter to the current sheet + readSheet = SheetUtils.match(readSheet, readSheetList, readAll, + context.readWorkbookHolder().getGlobalConfiguration()); + if (readSheet != null) { + if (readSheet.getSheetNo() != 0) { + // Prompt for the end of the previous form read + context.readSheetHolder().notifyAfterAllAnalysed(context); + } + context.currentSheet(readSheet); context.readWorkbookHolder().setIgnoreRecord03(Boolean.FALSE); - } else { - context.readWorkbookHolder().setIgnoreRecord03(Boolean.TRUE); } } sheetIndex++; @@ -71,6 +96,11 @@ public class BofRecordHandler extends AbstractXlsRecordHandler { } } + public void init(List readSheetList, Boolean readAll) { + this.readSheetList = readSheetList; + this.readAll = readAll; + } + @Override public int getOrder() { return 0; diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java index 7a34e6c..dd468b4 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -15,6 +15,7 @@ import javax.xml.parsers.SAXParserFactory; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFRelation; @@ -33,6 +34,8 @@ import com.alibaba.excel.read.metadata.ReadSheet; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.SheetUtils; +import com.alibaba.excel.util.StringUtils; /** * @@ -172,9 +175,16 @@ public class XlsxSaxAnalyser implements ExcelReadExecutor { } @Override - public void execute() { - parseXmlSource(sheetMap.get(analysisContext.readSheetHolder().getSheetNo()), - new XlsxRowHandler(analysisContext, stylesTable)); + public void execute(List readSheetList, Boolean readAll) { + for (ReadSheet readSheet : sheetList) { + readSheet = SheetUtils.match(readSheet, readSheetList, readAll, + analysisContext.readWorkbookHolder().getGlobalConfiguration()); + if (readSheet != null) { + analysisContext.currentSheet(readSheet); + parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(analysisContext, stylesTable)); + // The last sheet is read + analysisContext.readSheetHolder().notifyAfterAllAnalysed(analysisContext); + } + } } - } diff --git a/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java b/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java new file mode 100644 index 0000000..1475ad3 --- /dev/null +++ b/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java @@ -0,0 +1,18 @@ +package com.alibaba.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ignore all unannotated fields. + * + * @author Jiaju Zhuang + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelIgnoreUnannotated { +} diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java index 5c918f2..f44c2b3 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -22,12 +22,10 @@ public interface AnalysisContext { /** * Select the current table * - * @param excelReadExecutor - * Excel file Executor * @param readSheet * sheet to read */ - void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet); + void currentSheet(ReadSheet readSheet); /** * All information about the workbook you are currently working on diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java index 999aa50..e5ded50 100644 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -5,8 +5,6 @@ import java.io.InputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.alibaba.excel.analysis.ExcelReadExecutor; -import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.read.metadata.ReadSheet; @@ -16,7 +14,6 @@ import com.alibaba.excel.read.metadata.holder.ReadRowHolder; import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.util.StringUtils; /** * @@ -53,13 +50,9 @@ public class AnalysisContextImpl implements AnalysisContext { } @Override - public void currentSheet(ExcelReadExecutor excelReadExecutor, ReadSheet readSheet) { - if (readSheet == null) { - throw new IllegalArgumentException("Sheet argument cannot be null."); - } + public void currentSheet(ReadSheet readSheet) { readSheetHolder = new ReadSheetHolder(readSheet, readWorkbookHolder); currentReadHolder = readSheetHolder; - selectSheet(excelReadExecutor); if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { throw new ExcelAnalysisException("Cannot read sheet repeatedly."); } @@ -69,54 +62,6 @@ public class AnalysisContextImpl implements AnalysisContext { } } - private void selectSheet(ExcelReadExecutor excelReadExecutor) { - if (excelReadExecutor instanceof XlsxSaxAnalyser) { - selectSheet07(excelReadExecutor); - } else { - selectSheet03(); - } - } - - private void selectSheet03() { - if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - return; - } - if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - LOGGER.warn("Excel 2003 does not support matching sheets by name, defaults to the first one."); - } - readSheetHolder.setSheetNo(0); - } - - private void selectSheet07(ExcelReadExecutor excelReadExecutor) { - if (readSheetHolder.getSheetNo() != null && readSheetHolder.getSheetNo() >= 0) { - for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { - if (readSheetExcel.getSheetNo().equals(readSheetHolder.getSheetNo())) { - readSheetHolder.setSheetName(readSheetExcel.getSheetName()); - return; - } - } - throw new ExcelAnalysisException("Can not find sheet:" + readSheetHolder.getSheetNo()); - } - if (!StringUtils.isEmpty(readSheetHolder.getSheetName())) { - for (ReadSheet readSheetExcel : excelReadExecutor.sheetList()) { - String sheetName = readSheetExcel.getSheetName(); - if (sheetName == null) { - continue; - } - if (readSheetHolder.globalConfiguration().getAutoTrim()) { - sheetName = sheetName.trim(); - } - if (sheetName.equals(readSheetHolder.getSheetName())) { - readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); - return; - } - } - } - ReadSheet readSheetExcel = excelReadExecutor.sheetList().get(0); - readSheetHolder.setSheetNo(readSheetExcel.getSheetNo()); - readSheetHolder.setSheetName(readSheetExcel.getSheetName()); - } - @Override public ReadWorkbookHolder readWorkbookHolder() { return readWorkbookHolder; diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java index a5e6d58..c6e85c9 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ b/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -69,18 +69,6 @@ public interface WriteContext { */ void finish(); - /** - * isEncrypt - * @return - */ - boolean isEncrypt(); - - /** - * setPassword - * @param password - * @return - */ - void setPassword(String password); /** * Current sheet diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index 8972424..ffacd52 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -1,15 +1,27 @@ package com.alibaba.excel.context; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; import com.alibaba.excel.util.StringUtils; + +import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.InputStream; import java.io.OutputStream; +import java.security.GeneralSecurityException; import java.util.Map; +import java.util.UUID; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.Decryptor; import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.crypt.EncryptionMode; import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.DocumentOutputStream; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -120,7 +132,6 @@ public class WriteContextImpl implements WriteContext { WriteHandlerUtils.beforeSheetCreate(this); // Initialization current sheet initSheet(writeType); - WriteHandlerUtils.afterSheetCreate(this); } private void initCurrentSheetHolder(WriteSheet writeSheet) { @@ -147,6 +158,7 @@ public class WriteContextImpl implements WriteContext { writeSheetHolder.setCachedSheet(currentSheet); } writeSheetHolder.setSheet(currentSheet); + WriteHandlerUtils.afterSheetCreate(this); if (WriteTypeEnum.ADD.equals(writeType)) { // Initialization head initHead(writeSheetHolder.excelWriteHeadProperty()); @@ -249,18 +261,23 @@ public class WriteContextImpl implements WriteContext { if (writeWorkbookHolder == null) { return; } - try { - writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); - if (isEncrypt()){ - LOGGER.debug("start encrypt"); - encrypt(writeWorkbookHolder); - LOGGER.debug("finish encrypt"); - } - writeWorkbookHolder.getWorkbook().close(); + boolean isOutputStreamEncrypt = false; + try { + isOutputStreamEncrypt = doOutputStreamEncrypt07(); } catch (Throwable t) { throwCanNotCloseIo(t); } + + if (!isOutputStreamEncrypt) { + try { + writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); + writeWorkbookHolder.getWorkbook().close(); + } catch (Throwable t) { + throwCanNotCloseIo(t); + } + } + try { Workbook workbook = writeWorkbookHolder.getWorkbook(); if (workbook instanceof SXSSFWorkbook) { @@ -269,6 +286,7 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } + try { if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { writeWorkbookHolder.getOutputStream().close(); @@ -276,6 +294,15 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } + + if (!isOutputStreamEncrypt) { + try { + doFileEncrypt07(); + } catch (Throwable t) { + throwCanNotCloseIo(t); + } + } + try { if (writeWorkbookHolder.getTempTemplateInputStream() != null) { writeWorkbookHolder.getTempTemplateInputStream().close(); @@ -283,21 +310,14 @@ public class WriteContextImpl implements WriteContext { } catch (Throwable t) { throwCanNotCloseIo(t); } + + clearEncrypt03(); + if (LOGGER.isDebugEnabled()) { LOGGER.debug("Finished write."); } } - @Override - public boolean isEncrypt() { - return !StringUtils.isEmpty(writeWorkbookHolder.getPassword()); - } - - @Override - public void setPassword(String password) { - writeWorkbookHolder.setPassword(password); - } - private void throwCanNotCloseIo(Throwable t) { throw new ExcelGenerateException("Can not close IO", t); } @@ -322,28 +342,94 @@ public class WriteContextImpl implements WriteContext { return writeWorkbookHolder.getWorkbook(); } + private void clearEncrypt03() { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLS.equals(writeWorkbookHolder.getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); + } + /** - * do encrypt - * @param writeWorkbookHolder + * To encrypt */ - private void encrypt(WriteWorkbookHolder writeWorkbookHolder){ + private boolean doOutputStreamEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return false; + } + if (writeWorkbookHolder.getFile() != null) { + return false; + } + File tempXlsx = FileUtils.createTmpFile(UUID.randomUUID().toString() + ".xlsx"); + FileOutputStream tempFileOutputStream = new FileOutputStream(tempXlsx); try { - POIFSFileSystem fs = new POIFSFileSystem(); - EncryptionInfo info = new EncryptionInfo(EncryptionMode.standard); - - Encryptor enc = info.getEncryptor(); - enc.confirmPassword(writeWorkbookHolder.getPassword()); - - OPCPackage opc = OPCPackage.open(writeWorkbookHolder.getFile(), PackageAccess.READ_WRITE); - OutputStream os = enc.getDataStream(fs); - opc.save(os); - opc.close(); - //write the encrypted file back to the stream - FileOutputStream fos = new FileOutputStream(writeWorkbookHolder.getFile()); - fs.writeFilesystem(fos); - fos.close(); - } catch (Exception e) { - e.printStackTrace(); + writeWorkbookHolder.getWorkbook().write(tempFileOutputStream); + } finally { + try { + writeWorkbookHolder.getWorkbook().close(); + tempFileOutputStream.close(); + } catch (Exception e) { + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + throw e; + } + } + POIFSFileSystem fileSystem = null; + try { + fileSystem = openFileSystemAndEncrypt(tempXlsx); + fileSystem.writeFilesystem(writeWorkbookHolder.getOutputStream()); + } finally { + if (fileSystem != null) { + fileSystem.close(); + } + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + } + return true; + } + + /** + * To encrypt + */ + private void doFileEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return; + } + if (writeWorkbookHolder.getFile() == null) { + return; + } + FileOutputStream fileOutputStream = null; + POIFSFileSystem fileSystem = null; + try { + fileSystem = openFileSystemAndEncrypt(writeWorkbookHolder.getFile()); + fileOutputStream = new FileOutputStream(writeWorkbookHolder.getFile()); + fileSystem.writeFilesystem(fileOutputStream); + } finally { + if (fileOutputStream != null) { + fileOutputStream.close(); + } + if (fileSystem != null) { + fileSystem.close(); + } + } + } + + private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception { + POIFSFileSystem fileSystem = new POIFSFileSystem(); + Encryptor encryptor = new EncryptionInfo(EncryptionMode.standard).getEncryptor(); + encryptor.confirmPassword(writeWorkbookHolder.getPassword()); + OPCPackage opcPackage = null; + try { + opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE); + OutputStream outputStream = encryptor.getDataStream(fileSystem); + opcPackage.save(outputStream); + } finally { + opcPackage.close(); } + return fileSystem; } } diff --git a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java index 89d7851..40bffe6 100644 --- a/src/main/java/com/alibaba/excel/metadata/BasicParameter.java +++ b/src/main/java/com/alibaba/excel/metadata/BasicParameter.java @@ -74,4 +74,5 @@ public class BasicParameter { public void setUse1904windowing(Boolean use1904windowing) { this.use1904windowing = use1904windowing; } + } 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 4bffce6..6921161 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -13,9 +13,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.converters.AutoConverter; import com.alibaba.excel.converters.Converter; import com.alibaba.excel.enums.HeadKindEnum; @@ -120,6 +122,8 @@ public class ExcelHeadProperty { tempClass = tempClass.getSuperclass(); } + ExcelIgnoreUnannotated excelIgnoreUnannotated = + (ExcelIgnoreUnannotated)headClazz.getAnnotation(ExcelIgnoreUnannotated.class); // Screening of field List defaultFieldList = new ArrayList(); Map customFiledMap = new TreeMap(); @@ -130,7 +134,9 @@ public class ExcelHeadProperty { continue; } ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); - if (excelProperty == null && convertAllFiled != null && !convertAllFiled) { + boolean noExcelProperty = excelProperty == null + && ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null); + if (noExcelProperty) { ignoreMap.put(field.getName(), field); continue; } diff --git a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java index 2b5bcd6..340548b 100644 --- a/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java +++ b/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -222,10 +222,28 @@ public class ExcelReaderBuilder { return this; } + /** + * Whether the encryption + * + * @param password + * @return + */ + public ExcelReaderBuilder password(String password) { + readWorkbook.setPassword(password); + return this; + } + public ExcelReader build() { return new ExcelReader(readWorkbook); } + public ExcelReader doReadAll() { + ExcelReader excelReader = build(); + excelReader.readAll(); + excelReader.finish(); + return excelReader; + } + public ExcelReaderSheetBuilder sheet() { return sheet(null, null); } diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java b/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java index f3a3a7b..5b6f8de 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java @@ -42,6 +42,19 @@ public class ReadSheet extends ReadBasicParameter { this.sheetName = sheetName; } + public void copyBasicParameter(ReadSheet other) { + if (other == null) { + return; + } + this.setHeadRowNumber(other.getHeadRowNumber()); + this.setCustomReadListenerList(other.getCustomReadListenerList()); + this.setHead(other.getHead()); + this.setClazz(other.getClazz()); + this.setCustomConverterList(other.getCustomConverterList()); + this.setAutoTrim(other.getAutoTrim()); + this.setUse1904windowing(other.getUse1904windowing()); + } + @Override public String toString() { return "ReadSheet{" + "sheetNo=" + sheetNo + ", sheetName='" + sheetName + '\'' + "} " + super.toString(); diff --git a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java index b21cdb8..3a873da 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java +++ b/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -59,6 +59,10 @@ public class ReadWorkbook extends ReadBasicParameter { * Select the cache.Default use {@link com.alibaba.excel.cache.selector.SimpleReadCacheSelector} */ private ReadCacheSelector readCacheSelector; + /** + * Whether the encryption + */ + private String password; /** * The default is all excel objects.Default is true. *

@@ -164,4 +168,12 @@ public class ReadWorkbook extends ReadBasicParameter { public void setReadCacheSelector(ReadCacheSelector readCacheSelector) { this.readCacheSelector = readCacheSelector; } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } } diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index 3fd8839..a94afb3 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -8,8 +8,6 @@ import java.util.Set; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.alibaba.excel.cache.ReadCache; import com.alibaba.excel.cache.selector.EternalReadCacheSelector; @@ -28,7 +26,6 @@ import com.alibaba.excel.support.ExcelTypeEnum; * @author Jiaju Zhuang */ public class ReadWorkbookHolder extends AbstractReadHolder { - private static final Logger LOGGER = LoggerFactory.getLogger(ReadWorkbookHolder.class); /** * current param @@ -82,7 +79,10 @@ public class ReadWorkbookHolder extends AbstractReadHolder { * Temporary files when reading excel */ private File tempFile; - + /** + * Whether the encryption + */ + private String password; /** * 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. @@ -174,6 +174,7 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } this.hasReadSheet = new HashSet(); this.ignoreRecord03 = Boolean.FALSE; + this.password = readWorkbook.getPassword(); } public ReadWorkbook getReadWorkbook() { @@ -312,6 +313,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder { this.ignoreRecord03 = ignoreRecord03; } + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + @Override public HolderEnum holderType() { return HolderEnum.WORKBOOK; diff --git a/src/main/java/com/alibaba/excel/util/FileUtils.java b/src/main/java/com/alibaba/excel/util/FileUtils.java index 9738b50..34e6853 100644 --- a/src/main/java/com/alibaba/excel/util/FileUtils.java +++ b/src/main/java/com/alibaba/excel/util/FileUtils.java @@ -157,6 +157,15 @@ public class FileUtils { return directory; } + public static File createTmpFile(String fileName) { + String tmpDir = System.getProperty(JAVA_IO_TMPDIR); + if (tmpDir == null) { + throw new RuntimeException( + "Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!"); + } + return new File(tmpDir, fileName); + } + /** * * @param directory diff --git a/src/main/java/com/alibaba/excel/util/SheetUtils.java b/src/main/java/com/alibaba/excel/util/SheetUtils.java new file mode 100644 index 0000000..66a1c48 --- /dev/null +++ b/src/main/java/com/alibaba/excel/util/SheetUtils.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.util; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Sheet utils + * + * @author Jiaju Zhuang + */ +public class SheetUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SheetUtils.class); + + private SheetUtils() {} + + /** + * Match the parameters to the actual sheet + * + * @param readSheet + * actual sheet + * @param parameterReadSheetList + * parameters + * @param readAll + * @return + */ + public static ReadSheet match(ReadSheet readSheet, List parameterReadSheetList, Boolean readAll, + GlobalConfiguration globalConfiguration) { + if (readAll) { + return readSheet; + } + for (ReadSheet parameterReadSheet : parameterReadSheetList) { + if (parameterReadSheet == null) { + continue; + } + if (parameterReadSheet.getSheetNo() == null && parameterReadSheet.getSheetName() == null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The first is read by default."); + } + parameterReadSheet.setSheetNo(0); + } + boolean match = (parameterReadSheet.getSheetNo() != null + && parameterReadSheet.getSheetNo().equals(readSheet.getSheetNo())); + if (!match) { + String parameterSheetName = parameterReadSheet.getSheetName(); + if (!StringUtils.isEmpty(parameterSheetName)) { + boolean autoTrim = (parameterReadSheet.getAutoTrim() != null && parameterReadSheet.getAutoTrim()) + || (parameterReadSheet.getAutoTrim() == null && globalConfiguration.getAutoTrim()); + if (autoTrim) { + parameterSheetName = parameterSheetName.trim(); + } + match = parameterSheetName.equals(readSheet.getSheetName()); + } + } + if (match) { + readSheet.copyBasicParameter(parameterReadSheet); + return readSheet; + } + } + return null; + } + +} diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index 949c3da..2f39e1c 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -1,10 +1,16 @@ package com.alibaba.excel.util; +import java.io.File; import java.io.IOException; +import java.io.OutputStream; -import org.apache.poi.hssf.usermodel.HSSFFont; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; @@ -48,6 +54,10 @@ public class WorkBookUtil { } writeWorkbookHolder.setCachedWorkbook(hssfWorkbook); writeWorkbookHolder.setWorkbook(hssfWorkbook); + if (writeWorkbookHolder.getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(writeWorkbookHolder.getPassword()); + hssfWorkbook.writeProtectWorkbook(writeWorkbookHolder.getPassword(), StringUtils.EMPTY); + } } public static Sheet createSheet(Workbook workbook, String sheetName) { diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index c1a00c2..fc017fd 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -103,7 +103,6 @@ public class ExcelBuilderImpl implements ExcelBuilder { } context.currentSheet(writeSheet, WriteTypeEnum.ADD); context.currentTable(writeTable); - context.setPassword(password); if (excelWriteAddExecutor == null) { excelWriteAddExecutor = new ExcelWriteAddExecutor(context); } 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 3faad69..82de038 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -90,6 +90,19 @@ public class ExcelWriterBuilder { return this; } + /** + * Whether the encryption. + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + * + * @param password + * @return + */ + public ExcelWriterBuilder password(String password) { + writeWorkbook.setPassword(password); + 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 95acbbc..26acff5 100644 --- a/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java +++ b/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -159,19 +159,6 @@ public class ExcelWriterSheetBuilder { excelWriter.finish(); } - /** - * write with password - * @param data - * @param password - */ - public void doWrite(List data,String password) { - if (excelWriter == null) { - throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); - } - excelWriter.write(data, build(), password); - excelWriter.finish(); - } - public ExcelWriterTableBuilder table() { return table(null); } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java index 8ea5209..ae6c495 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java +++ b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java @@ -50,6 +50,13 @@ public class WriteWorkbook extends WriteBasicParameter { * Mandatory use 'inputStream' .Default is false */ private Boolean mandatoryUseInputStream; + /** + * Whether the encryption + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + * + */ + private String password; /** * The default is all excel objects.Default is true. *

@@ -140,4 +147,12 @@ public class WriteWorkbook extends WriteBasicParameter { public void setWriteHandler(com.alibaba.excel.event.WriteHandler writeHandler) { this.writeHandler = writeHandler; } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } } diff --git a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java index 65559db..d802085 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -1,15 +1,12 @@ package com.alibaba.excel.write.metadata.holder; -import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -96,9 +93,8 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { * prevent duplicate creation of sheet objects */ private Map hasBeenInitializedSheet; - /** - * password + * Whether the encryption */ private String password; @@ -140,6 +136,7 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream(); } this.hasBeenInitializedSheet = new HashMap(); + this.password = writeWorkbook.getPassword(); } private void copyTemplate() throws IOException { @@ -257,11 +254,6 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { this.mandatoryUseInputStream = mandatoryUseInputStream; } - @Override - public HolderEnum holderType() { - return HolderEnum.WORKBOOK; - } - public String getPassword() { return password; } @@ -269,4 +261,9 @@ public class WriteWorkbookHolder extends AbstractWriteHolder { public void setPassword(String password) { this.password = password; } + + @Override + public HolderEnum holderType() { + return HolderEnum.WORKBOOK; + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java new file mode 100644 index 0000000..19ef8d0 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java @@ -0,0 +1,14 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.Data; + +/** + * @author Jiaju Zhuang + */ +@Data +public class EncryptData { + @ExcelProperty("姓名") + private String name; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java new file mode 100644 index 0000000..0fb603f --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson.JSON; + +/** + * @author Jiaju Zhuang + */ +public class EncryptDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(EncryptDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(EncryptData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assert.assertEquals(list.size(), 10); + Assert.assertEquals(list.get(0).getName(), "姓名0"); + Assert.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assert.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java new file mode 100644 index 0000000..7477887 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -0,0 +1,88 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.easyexcel.test.core.simple.SimpleDataSheetNameListener; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; + +/** + * + * @author Jiaju Zhuang + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class EncryptDataTest { + + private static File file07; + private static File file03; + private static File file07OutputStream; + private static File file03OutputStream; + + @BeforeClass + public static void init() { + file07 = TestFileUtil.createNewFile("encrypt07.xlsx"); + file03 = TestFileUtil.createNewFile("encrypt03.xls"); + file07OutputStream = TestFileUtil.createNewFile("encryptOutputStream07.xlsx"); + file03OutputStream = TestFileUtil.createNewFile("encryptOutputStream03.xls"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t01ReadAndWriteStream07() throws Exception { + readAndWriteStream(file07OutputStream, ExcelTypeEnum.XLSX); + } + + @Test + public void t02ReadAndWriteStream03() throws Exception { + readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLSX); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, EncryptData.class).password("123456").sheet().doWrite(data()); + EasyExcel.read(file, EncryptData.class, new EncryptDataListener()).password("123456").sheet().doRead(); + } + + private void readAndWriteStream(File file, ExcelTypeEnum excelType) throws Exception { + FileOutputStream fileOutputStream = new FileOutputStream(file); + EasyExcel.write(fileOutputStream, EncryptData.class).password("123456").excelType(excelType).sheet() + .doWrite(data()); + fileOutputStream.close(); + + FileInputStream fileInputStream = new FileInputStream(file); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456").sheet() + .doRead(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java index 579a821..44e2f55 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java @@ -40,6 +40,16 @@ public class MultipleSheetsDataTest { read(file03); } + @Test + public void t03Read07All() { + readAll(file07); + } + + @Test + public void t04Read03All() { + readAll(file03); + } + private void read(File file) { MultipleSheetsListener multipleSheetsListener = new MultipleSheetsListener(); ExcelReader excelReader = EasyExcel.read(file, MultipleSheetsData.class, multipleSheetsListener).build(); @@ -53,4 +63,8 @@ public class MultipleSheetsDataTest { excelReader.finish(); } + private void readAll(File file) { + EasyExcel.read(file, MultipleSheetsData.class, new MultipleSheetsListener()).doReadAll(); + } + } diff --git a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java index 950f562..f414be2 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java @@ -27,6 +27,7 @@ public class MultipleSheetsListener extends AnalysisEventListener * 1. 创建excel对应的实体对象 参照{@link DemoData} *

@@ -82,24 +82,21 @@ public class ReadTest { */ @Test public void repeatedRead() { - // 方法1 如果 sheet1 sheet2 都是同一数据 监听器和头 都写到最外层 String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet1 = EasyExcel.readSheet(0).build(); - ReadSheet readSheet2 = EasyExcel.readSheet(1).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); + // 读取全部sheet + // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); - // 方法2 如果 sheet1 sheet2 数据不一致的话 + // 读取部分sheet fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - excelReader = EasyExcel.read(fileName).build(); + ExcelReader excelReader = EasyExcel.read(fileName).build(); // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - readSheet1 = EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - readSheet2 = EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - excelReader.read(readSheet1); - excelReader.read(readSheet2); + ReadSheet readSheet1 = + EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + ReadSheet readSheet2 = + EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能 + excelReader.read(readSheet1, readSheet2); // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 excelReader.finish(); } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java index b65fbf0..8a7902b 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java @@ -2,6 +2,8 @@ package com.alibaba.easyexcel.test.demo.write; import java.util.Date; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; @@ -19,4 +21,9 @@ public class DemoData { private Date date; @ExcelProperty("数字标题") private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index b2a1722..336a199 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -57,7 +57,7 @@ public class WriteTest { // 这里 需要指定写用哪个class去读 ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - excelWriter.write(data(), writeSheet,"12345"); + excelWriter.write(data(), writeSheet); // 进行加密 /// 千万别忘记finish 会帮忙关闭流 excelWriter.finish(); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java new file mode 100644 index 0000000..2ff3427 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java @@ -0,0 +1,73 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellCopyPolicy; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Ignore; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.util.FileUtils; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ +@Ignore +public class Poi3Test { + private static final Logger LOGGER = LoggerFactory.getLogger(Poi3Test.class); + + @Test + public void Encryption() throws Exception { + String file = TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx"; + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); + Encryptor enc = info.getEncryptor(); + enc.confirmPassword("foobaa"); + OPCPackage opc = OPCPackage.open(new File(file), PackageAccess.READ_WRITE); + OutputStream os = enc.getDataStream(fs); + opc.save(os); + opc.close(); + + // Write out the encrypted version + FileOutputStream fos = new FileOutputStream("D:\\test\\99999999999.xlsx"); + fs.writeFilesystem(fos); + fos.close(); + fs.close(); + + } + + @Test + public void Encryption2() throws Exception { + Biff8EncryptionKey.setCurrentUserPassword("123456"); + POIFSFileSystem fs = new POIFSFileSystem(new File("d:/test/simple03.xls"), true); + HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true); + Biff8EncryptionKey.setCurrentUserPassword(null); + System.out.println(hwb.getSheetAt(0).getSheetName()); + + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 0a19c35..ee7be1e 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -22,6 +22,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; import com.alibaba.excel.util.FileUtils; /** diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java index d5a39d6..27bea33 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/simple/Wirte.java @@ -33,7 +33,7 @@ public class Wirte { String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 - EasyExcel.write(fileName).sheet("模板").head(head()).doWrite(data()); + EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); } private List> head() { diff --git a/update.md b/update.md index 04eec04..331dfbe 100644 --- a/update.md +++ b/update.md @@ -2,10 +2,13 @@ * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 -* 新增写支持 禁用头样式 autoHeadStyle +* 新增写支持 禁用头样式 `autoHeadStyle` * 用map读取数据 空的单元格也会有个 null的数据 * 转换报错 能获取到对应的行号和列号 * 优化读取全部sheet方案 +* 新增注解`ExcelIgnoreUnannotated` 支持忽略未加`ExcelProperty`注解的字段 +* 支持导出加密 [Issue #361](https://github.com/alibaba/easyexcel/issues/361) +* 支持导入加密 [Issue #295](https://github.com/alibaba/easyexcel/issues/295) # 2.0.5 * 优化07版超大文件读取方案 From 2320becd475624cfa58df9b3b5ec876b218720ad Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Tue, 15 Oct 2019 20:34:09 +0800 Subject: [PATCH 14/17] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=85=A8=E9=83=A8sheet=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- docs/API.md | 5 + problem.md | 116 -- quickstart.md | 1365 +---------------- .../excel/analysis/ExcelAnalyserImpl.java | 2 +- .../metadata/holder/ReadWorkbookHolder.java | 10 +- .../alibaba/excel/support/ExcelTypeEnum.java | 8 +- .../excel/write/metadata/WriteWorkbook.java | 2 +- .../test/core/encrypt/EncryptDataTest.java | 6 +- .../easyexcel/test/temp/Lock2Test.java | 2 +- update.md | 2 +- 11 files changed, 30 insertions(+), 1497 deletions(-) delete mode 100644 problem.md diff --git a/README.md b/README.md index bf9871a..535d1e5 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ easyexcel [![Maven central](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/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) +[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) +[官方网站: https://alibaba-easyexcel.github.io/](https://alibaba-easyexcel.github.io/) # 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模式。在上层做了模型转换的封装,让使用者更加简单方便 @@ -15,11 +16,9 @@ Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都 ![img](img/readme/large.png) ## 相关文档 -* [快速使用](/quickstart.md) +* [快速使用](https://alibaba-easyexcel.github.io/) * [关于软件](/abouteasyexcel.md) -* [常见问题](/problem.md) * [更新记事](/update.md) -* [English-README](/easyexcel_en.md) ## 维护者 姬朋飞(玉霄) diff --git a/docs/API.md b/docs/API.md index 032331a..1363584 100644 --- a/docs/API.md +++ b/docs/API.md @@ -12,6 +12,7 @@ * `ExcelIgnore` 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段 * `DateTimeFormat` 日期转换,用`String`去接收excel日期格式的数据会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` * `NumberFormat` 数字转换,用`String`去接收excel数字格式的数据会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 ### 参数 #### 通用参数 `ReadWorkbook`,`ReadSheet` 都会有的参数,如果为空,默认使用上级。 @@ -21,6 +22,7 @@ * `head` 与`clazz`二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。 * `clazz` 与`head`二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。 * `autoTrim` 字符串、表头等数据自动trim +* `password` 读的时候是否需要使用密码 #### ReadWorkbook(理解成excel对象)参数 * `excelType` 当前excel的类型 默认会自动判断 * `inputStream` 与`file`二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用`file`参数。因为使用了`inputStream` easyexcel会帮忙创建临时文件,最终还是`file` @@ -36,6 +38,7 @@ * `ExcelIgnore` 默认所有字段都会写入excel,这个注解会忽略这个字段 * `DateTimeFormat` 日期转换,将`Date`写到excel会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` * `NumberFormat` 数字转换,用`Number`写excel会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 ### 参数 #### 通用参数 `WriteWorkbook`,`WriteSheet` ,`WriteTable`都会有的参数,如果为空,默认使用上级。 @@ -53,6 +56,8 @@ * `templateInputStream` 模板的文件流 * `templateFile` 模板文件 * `autoCloseStream` 自动关闭流。 +* `password` 写的时候是否需要使用密码 +* `useDefaultStyle` 写的时候是否是使用默认头 #### WriteSheet(就是excel的一个Sheet)参数 * `sheetNo` 需要写入的编码。默认0 * `sheetName` 需要些的Sheet名称,默认同`sheetNo` diff --git a/problem.md b/problem.md deleted file mode 100644 index 9244148..0000000 --- a/problem.md +++ /dev/null @@ -1,116 +0,0 @@ -# 常见问题汇总 - -## 1、系统环境变量缺失或JDK版本不支持 - -``` -java.lang.NullPointerException - at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) - at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) - at sun.awt.FontConfiguration.init(FontConfiguration.java:107) - at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) - at sun.font.SunFontManager$2.run(SunFontManager.java:431) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.SunFontManager.(SunFontManager.java:376) - at sun.awt.FcFontManager.(FcFontManager.java:35) - at sun.awt.X11FontManager.(X11FontManager.java:57) - at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) - at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) - at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) - at java.lang.reflect.Constructor.newInstance(Constructor.java:423) - at java.lang.Class.newInstance(Class.java:442) - at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74) - at java.awt.Font.getFont2D(Font.java:495) - at java.awt.Font.canDisplayUpTo(Font.java:2080) - at java.awt.font.TextLayout.singleFont(TextLayout.java:470) - at java.awt.font.TextLayout.(TextLayout.java:531) - at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidth(SheetUtil.java:275) - at org.apache.poi.xssf.streaming.AutoSizeColumnTracker.(AutoSizeColumnTracker.java:117) - at org.apache.poi.xssf.streaming.SXSSFSheet.(SXSSFSheet.java:79) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(SXSSFWorkbook.java:656) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:677) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:83) - at com.alibaba.excel.write.context.GenerateContextImpl.buildCurrentSheet(GenerateContextImpl.java:93) - at com.alibaba.excel.write.ExcelBuilderImpl.addContent(ExcelBuilderImpl.java:53) - at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:44) -``` - -### 解决方法 - -该异常由于自己的环境变量缺少swing需要的字体配置,检查自己应用是否有配置-Djava.awt.headless=true,如果没有请加上该系统参数,可以解决问题。如果仍旧不行,在dockerfile中增加字体安装命令: -![粘贴图片.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/a857edfbc8199db7bb35b9e99f1f57d5.png) -参考: -https://lark.alipay.com/aone355606/gfqllg/ulptif -https://stackoverflow.com/questions/30626136/cannot-load-font-in-jre-8 http://www.jianshu.com/p/c05b5fc71bd0 -## 2、xls格式错用xlsx方式解析 - -``` -org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.verifyZipHeader(ZipHelper.java:172) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(ZipHelper.java:229) - at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:97) - at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:342) - at com.alibaba.excel.read.v07.XlsxSaxAnalyser.(XlsxSaxAnalyser.java:46) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:30) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v07.Read2007Xlsx.noModel(Read2007Xlsx.java:42) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) - at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.executeTest(BlockJUnit4ClassRunnerDecorator.java:126) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.invokeExplosively(BlockJUnit4ClassRunnerDecorator.java:104) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java) - at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) - at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) - at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) - at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) - at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) - at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) - at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) - at org.junit.runners.ParentRunner.run(ParentRunner.java:363) - at org.junit.runner.JUnitCore.run(JUnitCore.java:137) - at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) - at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) - at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237) - at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) -``` - -### 解决方法 - -该异常时由于03版的xls,文件用07版的方式做解析的报错,请检查excelType是否设置错误。或者是不是手动去修改了excel文件名后缀的xls为xlsx。 - -## 3、xlsx错用xls解析 - -``` -org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:152) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:140) - at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.(NPOIFSFileSystem.java:302) - at org.apache.poi.poifs.filesystem.POIFSFileSystem.(POIFSFileSystem.java:87) - at com.alibaba.excel.read.v03.XlsSaxAnalyser.(XlsSaxAnalyser.java:55) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:27) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v03.XLS2003FunctionTest.testExcel2003NoModel(XLS2003FunctionTest.java:31) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runTest(JUnitTestCaseDecorator.java:142) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.originalRunBare(JUnitTestCaseDecorator.java:102) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runBare(JUnitTestCaseDecorator.java:87) -``` -原理和原因大致同上 \ No newline at end of file diff --git a/quickstart.md b/quickstart.md index c9fdd7d..dcf12ad 100644 --- a/quickstart.md +++ b/quickstart.md @@ -1,1363 +1,2 @@ -# easyexcel核心功能 -## 各位读取文件务必使用2.0.3起 -2.0.0-beta1到2.0.2有小概率会丢失数字。 -## 目录 -### 前言 -#### 以下功能目前不支持 -* 单个文件的并发写入、读取 -* 读取图片 -* 宏 -* csv读取(这个后续可能会考虑) -#### 常见问题 -* 关于@Data,读写的对象都用到了[Lombok](https://www.projectlombok.org/),他会自动生成`get`,`set` ,如果不需要的话,自己创建对象并生成`get`,`set` 。 -* 出现`NoSuchMethodException`,`ClassNotFoundException`,`NoClassDefFoundError`极大概率是jar冲突,建议`clean`项目,或者统一`poi` 的版本,理论上来说`easyexcel`兼容poi的`3.17`,`4.0.1`,`4.1.0`所有较新版本 -* 如果在读的时候`Listener`里面需要使用spring的`@Autowired`,给`Listener`创建成员变量,然后在构造方法里面传进去。而别必须不让spring管理`Listener`,每次读取都要`new`一个。 -* 如果用`String`去接收数字,出现小数点等情况,这个是BUG,但是很难修复,后续版本会修复这个问题。目前请使用`@NumberFormat`注解,里面的参数就是调用了java自带的`NumberFormat.format`方法,不知道怎么入参的可以自己网上查询。 -* 10M以上xlsx读取很慢或者内存占用大 请先阅读[10M以上文件读取说明](/docs/LARGEREAD.md) -#### 详细参数介绍 -有些参数不知道怎么用,或者有些功能不知道用什么参数,参照:[详细参数介绍](/docs/API.md) -#### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★Star。 -### 读 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java) -* [最简单的读](#simpleRead) -* [指定列的下标或者列名](#indexOrNameRead) -* [读多个或者全部sheet](#repeatedRead) -* [日期、数字或者自定义格式转换](#converterRead) -* [多行头](#complexHeaderRead) -* [同步的返回](#synchronousRead) -* [读取表头数据](#headerRead) -* [数据转换等异常处理](#exceptionRead) -* [web中的读](#webRead) - -### 写 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java) -* [最简单的写](#simpleWrite) -* [指定写入的列](#indexWrite) -* [复杂头写入](#complexHeadWrite) -* [重复多次写入(包括不同sheet)](#repeatedWrite) -* [日期、数字或者自定义格式转换](#converterWrite) -* [图片导出](#imageWrite) -* [根据模板写入](#templateWrite) -* [列宽、行高](#widthAndHeightWrite) -* [自定义样式](#styleWrite) -* [合并单元格](#mergeWrite) -* [使用table去写入](#tableWrite) -* [动态头,实时生成头写入](#dynamicHeadWrite) -* [自动列宽(不太精确)](#longestMatchColumnWidthWrite) -* [自定义拦截器(下拉,超链接等上面几点都不符合但是要对单元格进行操作的参照这个)](#customHandlerWrite) -* [web中的写](#webWrite) - -### 填充 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java) -* [最简单的填充](#simpleFill) -* [填充列表](#listFill) -* [复杂的填充](#complexFill) -* [数据量大的复杂填充](#complexFillWithTable) -* [横向的填充](#horizontalFill) - -## 读excel样例 -### 最简单的读 -##### excel示例 -![img](img/readme/quickstart/read/demo.png) -##### 对象 -```java -@Data -public class DemoData { - private String string; - private Date date; - private Double doubleData; -} -``` -##### 监听器 -```java -// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 -public class DemoDataListener extends AnalysisEventListener { - private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); - /** - * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 - */ - private static final int BATCH_COUNT = 5; - List list = new ArrayList(); - - @Override - public void invoke(DemoData data, AnalysisContext context) { - LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); - list.add(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("存储数据库成功!"); - } -} -``` -##### 代码 -```java - /** - * 最简单的读 - *

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

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

3. 直接读即可 - */ - @Test - public void simpleRead() { - // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 - // 写法1: - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 - EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); - - // 写法2: - fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build(); - ReadSheet readSheet = EasyExcel.readSheet(0).build(); - excelReader.read(readSheet); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); - } -``` - -### 指定列的下标或者列名 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -```java -@Data -public class IndexOrNameData { - /** - * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 - */ - @ExcelProperty(index = 2) - private Double doubleData; - /** - * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 - */ - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题") - private Date date; -} -``` -##### 监听器 -参照:[监听器](#simpleReadListener) 只是泛型变了而已 -##### 代码 -```java - /** - * 指定列的下标或者列名 - * - *

1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData} - *

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

3. 直接读即可 - */ - @Test - public void indexOrNameRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里默认读取第一个sheet - EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead(); - } -``` - -### 读多个sheet -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 监听器 -参照:[监听器](#simpleReadListener) -##### 代码 -```java - /** - * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 - *

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

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

- * 3. 直接读即可 - */ - @Test - public void repeatedRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 读取全部sheet - // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 - EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); - - // 读取部分sheet - fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - ExcelReader excelReader = EasyExcel.read(fileName).build(); - // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener - ReadSheet readSheet1 = - EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - ReadSheet readSheet2 = - EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); - // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能 - excelReader.read(readSheet1, readSheet2); - // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 - excelReader.finish(); - } -``` - -### 日期、数字或者自定义格式转换 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -```java -@Data -public class ConverterData { - /** - * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:” - */ - @ExcelProperty(converter = CustomStringStringConverter.class) - private String string; - /** - * 这里用string 去接日期才能格式化。我想接收年月日格式 - */ - @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") - private String date; - /** - * 我想接收百分比的数字 - */ - @NumberFormat("#.##%") - private String doubleData; -} -``` -##### 监听器 -参照:[监听器](#simpleReadListener) 只是泛型变了 -##### 自定义转换器 -````java -public class CustomStringStringConverter implements Converter { - @Override - public Class supportJavaTypeKey() { - return String.class; - } - - @Override - public CellDataTypeEnum supportExcelTypeKey() { - return CellDataTypeEnum.STRING; - } - - /** - * 这里读的时候会调用 - * - * @param cellData - * NotNull - * @param contentProperty - * Nullable - * @param globalConfiguration - * NotNull - * @return - */ - @Override - public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - return "自定义:" + cellData.getStringValue(); - } - - /** - * 这里是写的时候会调用 不用管 - * - * @param value - * NotNull - * @param contentProperty - * Nullable - * @param globalConfiguration - * NotNull - * @return - */ - @Override - public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, - GlobalConfiguration globalConfiguration) { - return new CellData(value); - } - -} -```` -##### 代码 -```java - /** - * 日期、数字或者自定义格式转换 - *

- * 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()} - *

1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 - *

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

3. 直接读即可 - */ - @Test - public void converterRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) - // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 - // 如果就想单个字段使用请使用@ExcelProperty 指定converter - // .registerConverter(new CustomStringStringConverter()) - // 读取sheet - .sheet().doRead(); - } -``` - -### 多行头 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 监听器 -参照:[监听器](#simpleReadListener) -##### 代码 -```java - /** - * 多行头 - * - *

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

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

3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数, - * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。 - */ - @Test - public void complexHeaderRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet() - // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 - .headRowNumber(1).doRead(); - } -``` - -### 同步的返回 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 代码 -```java - /** - * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面 - */ - @Test - public void synchronousRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish - List list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync(); - for (Object obj : list) { - DemoData data = (DemoData)obj; - LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); - } - - // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish - list = EasyExcel.read(fileName).sheet().doReadSync(); - for (Object obj : list) { - // 返回每条数据的键值对 表示所在的列 和所在列的值 - Map data = (Map)obj; - LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); - } - } -``` - -### 读取表头数据 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) -##### 监听器 -参照:[监听器](#simpleReadListener) -里面多了一个方法,只要重写invokeHeadMap方法即可 -```java - /** - * 这里会一行行的返回头 - * - * @param headMap - * @param context - */ - @Override - public void invokeHeadMap(Map headMap, AnalysisContext context) { - LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); - } -``` -##### 代码 -```java - /** - * 读取表头数据 - * - *

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

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

- * 3. 直接读即可 - */ - @Test - public void headerRead() { - String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - // 这里 需要指定读用哪个class去读,然后读取第一个sheet - EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); - } -``` - -### 数据转换等异常处理 -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -```java -@Data -public class ExceptionDemoData { - /** - * 用日期去接字符串 肯定报错 - */ - private Date date; -} -``` -##### 监听器 -参照:[监听器](#simpleReadListener) -里面多了一个方法,只要重写onException方法即可 -```java - /** - * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 - * - * @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()); - } - } -``` -##### 代码 -```java - /** - * 数据转换等异常处理 - * - *

- * 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, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead(); - } -``` - - -### web中的读 -##### 示例代码 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) -##### excel示例 -参照:[excel示例](#simpleReadExcel) -##### 对象 -参照:[对象](#simpleReadObject) 只是名字变了 -##### 监听器 -参照:[监听器](#simpleReadListener) 只是泛型变了 -##### 代码 -```java - /** - * 文件上传 - *

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

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

3. 直接读即可 - */ - @PostMapping("upload") - @ResponseBody - public String upload(MultipartFile file) throws IOException { - EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener()).sheet().doRead(); - return "success"; - } -``` - -## 写excel样例 -### 通用数据生成 后面不会重复写 -```java - private List data() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - DemoData data = new DemoData(); - data.setString("字符串" + i); - data.setDate(new Date()); - data.setDoubleData(0.56); - list.add(data); - } - return list; - } -``` -### 最简单的写 -##### excel示例 -![img](img/readme/quickstart/write/simpleWrite.png) -##### 对象 -```java -@Data -public class DemoData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题") - private Date date; - @ExcelProperty("数字标题") - private Double doubleData; - /** - * 忽略这个字段 - */ - @ExcelIgnore - private String ignore; -} -``` -##### 代码 -```java - /** - * 最简单的写 - *

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

2. 直接写即可 - */ - @Test - public void simpleWrite() { - // 写法1 - String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - // 如果这里想使用03 则 传入excelType参数即可 - EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); - - // 写法2 - fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - excelWriter.write(data(), writeSheet); - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 指定写入的列 -##### excel示例 -![img](img/readme/quickstart/write/indexWrite.png) -##### 对象 -```java -@Data -public class IndexData { - @ExcelProperty(value = "字符串标题", index = 0) - private String string; - @ExcelProperty(value = "日期标题", index = 1) - private Date date; - /** - * 这里设置3 会导致第二列空的 - */ - @ExcelProperty(value = "数字标题", index = 3) - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 指定写入的列 - *

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

2. 使用{@link ExcelProperty}注解指定写入的列 - *

3. 直接写即可 - */ - @Test - public void indexWrite() { - String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); - } -``` - -### 复杂头写入 -##### excel示例 -![img](img/readme/quickstart/write/complexHeadWrite.png) -##### 对象 -```java -@Data -public class ComplexHeadData { - @ExcelProperty({"主标题", "字符串标题"}) - private String string; - @ExcelProperty({"主标题", "日期标题"}) - private Date date; - @ExcelProperty({"主标题", "数字标题"}) - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 复杂头写入 - *

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

2. 使用{@link ExcelProperty}注解指定复杂的头 - *

3. 直接写即可 - */ - @Test - public void complexHeadWrite() { - String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data()); - } -``` - -### 重复多次写入 -##### excel示例 -![img](img/readme/quickstart/write/repeatedWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 重复多次写入 - *

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

- * 2. 使用{@link ExcelProperty}注解指定复杂的头 - *

- * 3. 直接调用二次写入即可 - */ - @Test - public void repeatedWrite() { - // 方法1 如果写到同一个sheet - String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 这里注意 如果同一个sheet只要创建一次 - WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来 - for (int i = 0; i < 5; i++) { - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - - // 方法2 如果写到不同的sheet 同一个对象 - fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 指定文件 - excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 - for (int i = 0; i < 5; i++) { - // 每次都要创建writeSheet 这里注意必须指定sheetNo - writeSheet = EasyExcel.writerSheet(i, "模板").build(); - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - - // 方法3 如果写到不同的sheet 不同的对象 - fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 指定文件 - excelWriter = EasyExcel.write(fileName).build(); - // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 - for (int i = 0; i < 5; i++) { - // 每次都要创建writeSheet 这里注意必须指定sheetNo。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变 - writeSheet = EasyExcel.writerSheet(i, "模板").head(DemoData.class).build(); - // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 - List data = data(); - excelWriter.write(data, writeSheet); - } - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 日期、数字或者自定义格式转换 -##### excel示例 -![img](img/readme/quickstart/write/converterWrite.png) -##### 对象 -```java -@Data -public class ConverterData { - /** - * 我想所有的 字符串起前面加上"自定义:"三个字 - */ - @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class) - private String string; - /** - * 我想写到excel 用年月日的格式 - */ - @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") - @ExcelProperty("日期标题") - private Date date; - /** - * 我想写到excel 用百分比表示 - */ - @NumberFormat("#.##%") - @ExcelProperty(value = "数字标题") - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 日期、数字或者自定义格式转换 - *

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

2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 - *

3. 直接写即可 - */ - @Test - public void converterWrite() { - String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data()); - } -``` - -### 图片导出 -##### excel示例 -![img](img/readme/quickstart/write/imageWrite.png) -##### 对象 -```java -@Data -@ContentRowHeight(100) -@ColumnWidth(100 / 8) -public class ImageData { - private File file; - private InputStream inputStream; - /** - * 如果string类型 必须指定转换器,string默认转换成string - */ - @ExcelProperty(converter = StringImageConverter.class) - private String string; - private byte[] byteArray; -} -``` -##### 代码 -```java - /** - * 图片导出 - *

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

2. 直接写即可 - */ - @Test - public void imageWrite() throws Exception { - String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; - // 如果使用流 记得关闭 - InputStream inputStream = null; - try { - List list = new ArrayList(); - ImageData imageData = new ImageData(); - list.add(imageData); - String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; - // 放入四种类型的图片 实际使用只要选一种即可 - imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); - imageData.setFile(new File(imagePath)); - imageData.setString(imagePath); - inputStream = FileUtils.openInputStream(new File(imagePath)); - imageData.setInputStream(inputStream); - EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list); - } finally { - if (inputStream != null) { - inputStream.close(); - } - } - } -``` - - -### 根据模板写入 -##### 模板excel示例 -参照:[模板excel示例](#simpleReadExcel) -##### excel示例 -![img](img/readme/quickstart/write/templateWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 根据模板写入 - *

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

2. 使用{@link ExcelProperty}注解指定写入的列 - *

3. 使用withTemplate 读取模板 - *

4. 直接写即可 - */ - @Test - public void templateWrite() { - String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; - String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).withTemplate(templateFileName).sheet().doWrite(data()); - } -``` - -### 列宽、行高 -##### excel示例 -![img](img/readme/quickstart/write/widthAndHeightWrite.png) -##### 对象 -````java -@Data -@ContentRowHeight(10) -@HeadRowHeight(20) -@ColumnWidth(25) -public class WidthAndHeightData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题") - private Date date; - /** - * 宽度为50 - */ - @ColumnWidth(50) - @ExcelProperty("数字标题") - private Double doubleData; -} -```` -##### 代码 -```java - /** - * 列宽、行高 - *

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

2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度 - *

3. 直接写即可 - */ - @Test - public void widthAndHeightWrite() { - String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data()); - } -``` - -### 自定义样式 -##### excel示例 -![img](img/readme/quickstart/write/styleWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 自定义样式 - *

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

2. 创建一个style策略 并注册 - *

3. 直接写即可 - */ - @Test - public void styleWrite() { - String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx"; - // 头的策略 - WriteCellStyle headWriteCellStyle = new WriteCellStyle(); - // 背景设置为红色 - headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); - WriteFont headWriteFont = new WriteFont(); - headWriteFont.setFontHeightInPoints((short)20); - headWriteCellStyle.setWriteFont(headWriteFont); - // 内容的策略 - WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); - // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 - contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); - // 背景绿色 - contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); - WriteFont contentWriteFont = new WriteFont(); - // 字体大小 - contentWriteFont.setFontHeightInPoints((short)20); - contentWriteCellStyle.setWriteFont(contentWriteFont); - // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 - HorizontalCellStyleStrategy horizontalCellStyleStrategy = - new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); - - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板") - .doWrite(data()); - } -``` - -### 合并单元格 -##### excel示例 -![img](img/readme/quickstart/write/mergeWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 合并单元格 - *

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

2. 创建一个merge策略 并注册 - *

3. 直接写即可 - */ - @Test - public void mergeWrite() { - String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; - // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写 - LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0); - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板") - .doWrite(data()); - } -``` - -### 使用table去写入 -##### excel示例 -![img](img/readme/quickstart/write/tableWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 使用table去写入 - *

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

2. 然后写入table即可 - */ - @Test - public void tableWrite() { - String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案例 - // 这里 需要指定写用哪个class去读 - ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); - // 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了 - WriteSheet writeSheet = EasyExcel.writerSheet("模板").needHead(Boolean.FALSE).build(); - // 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要 - WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).build(); - WriteTable writeTable1 = EasyExcel.writerTable(1).needHead(Boolean.TRUE).build(); - // 第一次写入会创建头 - excelWriter.write(data(), writeSheet, writeTable0); - // 第二次写如也会创建头,然后在第一次的后面写入数据 - excelWriter.write(data(), writeSheet, writeTable1); - /// 千万别忘记finish 会帮忙关闭流 - excelWriter.finish(); - } -``` - -### 动态头,实时生成头写入 -##### excel示例 -![img](img/readme/quickstart/write/dynamicHeadWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 代码 -```java - /** - * 动态头,实时生成头写入 - *

- * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据 - * - *

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

- * 2. 然后写入table即可 - */ - @Test - public void dynamicHeadWrite() { - String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; - EasyExcel.write(fileName) - // 这里放入动态头 - .head(head()).sheet("模板") - // 当然这里数据也可以用 List> 去传入 - .doWrite(data()); - } - - private List> head() { - List> list = new ArrayList>(); - List head0 = new ArrayList(); - head0.add("字符串" + System.currentTimeMillis()); - List head1 = new ArrayList(); - head1.add("数字" + System.currentTimeMillis()); - List head2 = new ArrayList(); - head2.add("日期" + System.currentTimeMillis()); - list.add(head0); - list.add(head1); - list.add(head2); - return list; - } -``` - -### 自动列宽(不太精确) -##### excel示例 -![img](img/readme/quickstart/write/longestMatchColumnWidthWrite.png) -##### 对象 -```java -@Data -public class LongestMatchColumnWidthData { - @ExcelProperty("字符串标题") - private String string; - @ExcelProperty("日期标题很长日期标题很长日期标题很长很长") - private Date date; - @ExcelProperty("数字") - private Double doubleData; -} -``` -##### 代码 -```java - /** - * 自动列宽(不太精确) - *

- * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 - * {@link LongestMatchColumnWidthStyleStrategy}重新实现. - *

- * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。 - * - *

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

- * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy} - *

- * 3. 直接写即可 - */ - @Test - public void longestMatchColumnWidthWrite() { - String fileName = - TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, LongestMatchColumnWidthData.class) - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong()); - } - - private List dataLong() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - LongestMatchColumnWidthData data = new LongestMatchColumnWidthData(); - data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i); - data.setDate(new Date()); - data.setDoubleData(1000000000000.0); - list.add(data); - } - return list; - } -``` - -### 自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个) -##### excel示例 -![img](img/readme/quickstart/write/customHandlerWrite.png) -##### 对象 -参照:[对象](#simpleWriteObject) -##### 定义拦截器 -````java -/** - * 自定义拦截器。对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel - * - * @author Jiaju Zhuang - */ -public class CustomCellWriteHandler implements CellWriteHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class); - - @Override - public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, - Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { - - } - - @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, Integer relativeRowIndex, Boolean isHead) { - - } - - @Override - public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, - List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { - // 这里可以对cell进行任何操作 - LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); - if (isHead && cell.getColumnIndex() == 0) { - CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper(); - Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL); - hyperlink.setAddress("https://github.com/alibaba/easyexcel"); - cell.setHyperlink(hyperlink); - } - } - -} -```` -````java -/** - * 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 - * - * @author Jiaju Zhuang - */ -public class CustomSheetWriteHandler implements SheetWriteHandler { - - private static final Logger LOGGER = LoggerFactory.getLogger(CustomSheetWriteHandler.class); - - @Override - public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - - } - - @Override - public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { - LOGGER.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo()); - - // 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行 - CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0); - DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); - DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"}); - DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); - writeSheetHolder.getSheet().addValidationData(dataValidation); - } -} - -```` -##### 代码 -```java - /** - * 下拉,超链接等自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个) - *

- * demo这里实现2点。1. 对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel 2. 对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 - *

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

- * 2. 注册拦截器 {@link CustomCellWriteHandler} {@link CustomSheetWriteHandler} - *

- * 2. 直接写即可 - */ - @Test - public void customHandlerWrite() { - String fileName = TestFileUtil.getPath() + "customHandlerWrite" + System.currentTimeMillis() + ".xlsx"; - // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 - EasyExcel.write(fileName, DemoData.class).registerWriteHandler(new CustomSheetWriteHandler()) - .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); - } -``` - -### web中的写 -##### 示例代码 -DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) -##### 对象 -参照:[对象](#simpleWriteObject) 就是名称变了下 -##### 代码 -```java - /** - * 文件下载 - *

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

- * 2. 设置返回的 参数 - *

- * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 - */ - @GetMapping("download") - public void download(HttpServletResponse response) throws IOException { - // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman - response.setContentType("application/vnd.ms-excel"); - response.setCharacterEncoding("utf-8"); - // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 - String fileName = URLEncoder.encode("测试", "UTF-8"); - response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); - EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); - } -``` - -## 填充excel样例 -### 最简单的填充 -##### 模板 -![img](img/readme/quickstart/fill/simpleFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/simpleFill.png) -##### 对象 -```java -@Data -public class FillData { - private String name; - private double number; -} -``` -##### 代码 -```java - /** - * 最简单的填充 - */ - @Test - public void simpleFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; - - // 方案1 根据对象填充 - String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - FillData fillData = new FillData(); - fillData.setName("张三"); - fillData.setNumber(5.2); - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); - - // 方案2 根据Map填充 - fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - Map map = new HashMap(); - map.put("name", "张三"); - map.put("number", 5.2); - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); - } -``` - -### 填充列表 -##### 模板 -![img](img/readme/quickstart/fill/listFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/listFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 填充列表 - */ - @Test - public void listFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // 填充list 的时候还要注意 模板中{.} 多了个点 表示list - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; - - // 方案1 一下子全部放到内存里面 并填充 - String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; - // 这里 会填充到第一个sheet, 然后文件流会自动关闭 - EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); - - // 方案2 分多次 填充 会使用文件缓存(省内存) - fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - excelWriter.fill(data(), writeSheet); - excelWriter.fill(data(), writeSheet); - // 千万别忘记关闭流 - excelWriter.finish(); - } -``` - -### 复杂的填充 -##### 模板 -![img](img/readme/quickstart/fill/complexFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/complexFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 复杂的填充 - */ - @Test - public void complexFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; - - String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 - // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 - // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 - // 如果数据量大 list不是最后一行 参照下一个 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - excelWriter.fill(data(), fillConfig, writeSheet); - excelWriter.fill(data(), fillConfig, writeSheet); - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - map.put("total", 1000); - excelWriter.fill(map, writeSheet); - excelWriter.finish(); - } -``` - -### 数据量大的复杂填充 -##### 模板 -![img](img/readme/quickstart/fill/complexFillWithTableTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/complexFillWithTable.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 数据量大的复杂填充 - *

- * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 - */ - @Test - public void complexFillWithTable() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - // 这里模板 删除了list以后的数据,也就是统计的这一行 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; - - String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - // 直接写入数据 - excelWriter.fill(data(), writeSheet); - excelWriter.fill(data(), writeSheet); - - // 写入list之前的数据 - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - excelWriter.fill(map, writeSheet); - - // list 后面还有个统计 想办法手动写入 - // 这里偷懒直接用list 也可以用对象 - List> totalListList = new ArrayList>(); - List totalList = new ArrayList(); - totalListList.add(totalList); - totalList.add(null); - totalList.add(null); - totalList.add(null); - // 第四列 - totalList.add("统计:1000"); - // 这里是write 别和fill 搞错了 - excelWriter.write(totalListList, writeSheet); - excelWriter.finish(); - // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 - // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 - } -``` - -### 横向的填充 -##### 模板 -![img](img/readme/quickstart/fill/horizontalFillTemplate.png) -##### 最终效果 -![img](img/readme/quickstart/fill/horizontalFill.png) -##### 对象 -参照:[对象](#simpleFillObject) -##### 代码 -```java - /** - * 横向的填充 - */ - @Test - public void horizontalFill() { - // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 - // {} 代表普通变量 {.} 代表是list的变量 - String templateFileName = - TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; - - String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; - ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); - WriteSheet writeSheet = EasyExcel.writerSheet().build(); - FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); - excelWriter.fill(data(), fillConfig, writeSheet); - excelWriter.fill(data(), fillConfig, writeSheet); - - Map map = new HashMap(); - map.put("date", "2019年10月9日13:28:28"); - excelWriter.fill(map, writeSheet); - - // 别忘记关闭流 - excelWriter.finish(); - } -``` - -## 测试数据分析 -![POI usermodel PK easyexcel(Excel 2003).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/02c4bfbbab99a649788523d04f84a42f.png) -![POI usermodel PK easyexcel(Excel 2007).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/f6a8a19ec959f0eb564e652de523fc9e.png) -![POI usermodel PK easyexcel(Excel 2003) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/26888f7ea1cb8dc56db494926544edf7.png) -![POI usermodel PK easyexcel(Excel 2007) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/4de1ac95bdfaa4b1870b224af4f4cb75.png) -从上面的性能测试可以看出easyexcel在解析耗时上比poiuserModel模式弱了一些。主要原因是我内部采用了反射做模型字段映射,中间我也加了cache,但感觉这点差距可以接受的。但在内存消耗上差别就比较明显了,easyexcel在后面文件再增大,内存消耗几乎不会增加了。但poi userModel就不一样了,简直就要爆掉了。想想一个excel解析200M,同时有20个人再用估计一台机器就挂了。 - +# 请登录官网查看 +官网地址:[https://alibaba-easyexcel.github.io](https://alibaba-easyexcel.github.io) \ No newline at end of file diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java index 3b4754d..6e1f435 100644 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -60,7 +60,7 @@ public class ExcelAnalyserImpl implements ExcelAnalyser { } switch (excelType) { case XLS: - POIFSFileSystem poifsFileSystem = null; + POIFSFileSystem poifsFileSystem; if (readWorkbookHolder.getFile() != null) { poifsFileSystem = new POIFSFileSystem(readWorkbookHolder.getFile()); } else { diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java index a94afb3..35b3859 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java +++ b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -141,11 +141,11 @@ public class ReadWorkbookHolder extends AbstractReadHolder { } else { this.autoCloseStream = readWorkbook.getAutoCloseStream(); } - if (readWorkbook.getExcelType() == null) { - this.excelType = ExcelTypeEnum.valueOf(file, inputStream); - } else { - this.excelType = readWorkbook.getExcelType(); - } + + // The type of excel is read according to the judgment.Because encrypted XLSX needs to be specified as XLS to + // properly parse. + this.excelType = ExcelTypeEnum.valueOf(file, inputStream, readWorkbook.getExcelType()); + if (ExcelTypeEnum.XLS == excelType && getGlobalConfiguration().getUse1904windowing() == null) { getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); } diff --git a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java index 2f02327..b4e41df 100644 --- a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java +++ b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java @@ -29,7 +29,7 @@ public enum ExcelTypeEnum { this.setValue(value); } - public static ExcelTypeEnum valueOf(File file, InputStream inputStream) { + public static ExcelTypeEnum valueOf(File file, InputStream inputStream, ExcelTypeEnum excelType) { try { FileMagic fileMagic; if (file != null) { @@ -59,9 +59,15 @@ public enum ExcelTypeEnum { return XLSX; } } catch (IOException e) { + if (excelType != null) { + return excelType; + } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself", e); } + if (excelType != null) { + return excelType; + } throw new ExcelCommonException( "Convert excel format exception.You can try specifying the 'excelType' yourself"); } diff --git a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java index ae6c495..7d9ebcd 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java +++ b/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java @@ -43,7 +43,7 @@ public class WriteWorkbook extends WriteBasicParameter { */ private File templateFile; /** - * Default true + * Default trueuseDefaultStyle */ private Boolean autoCloseStream; /** diff --git a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java index 7477887..98f580f 100644 --- a/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -57,7 +57,7 @@ public class EncryptDataTest { @Test public void t02ReadAndWriteStream03() throws Exception { - readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLSX); + readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLS); } private void readAndWrite(File file) { @@ -72,8 +72,8 @@ public class EncryptDataTest { fileOutputStream.close(); FileInputStream fileInputStream = new FileInputStream(file); - EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456").sheet() - .doRead(); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456") + .excelType(excelType).sheet().doRead(); } private List data() { diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java index cba9bae..052b60b 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -29,7 +29,7 @@ public class Lock2Test { @Test public void test() throws Exception { - File file = new File("D:\\test\\test001.xlsx"); + File file = new File("D:\\test\\000001.xlsx"); List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); LOGGER.info("数据:{}", list.size()); diff --git a/update.md b/update.md index 331dfbe..2ae7d28 100644 --- a/update.md +++ b/update.md @@ -2,7 +2,7 @@ * 新增支持导入、导出支持公式 * 新增支持读取单元格类型、写入指定单元格类型 * 支持通过模板填充数据 -* 新增写支持 禁用头样式 `autoHeadStyle` +* 新增写支持 禁用头样式 `useDefaultStyle` * 用map读取数据 空的单元格也会有个 null的数据 * 转换报错 能获取到对应的行号和列号 * 优化读取全部sheet方案 From b675ba50c522815b3b0c6241ce07b13ebf1cf83c Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Wed, 16 Oct 2019 19:46:36 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E7=9A=84demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/demo/read/NoModleDataListener.java | 50 +++++++++++++++++++ .../easyexcel/test/demo/read/ReadTest.java | 9 ++++ .../easyexcel/test/demo/write/WriteTest.java | 24 ++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.java new file mode 100644 index 0000000..2e77538 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModleDataListener.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.fastjson.JSON; + +/** + * 直接用map接收数据 + * + * @author Jiaju Zhuang + */ +public class NoModleDataListener extends AnalysisEventListener> { + private static final Logger LOGGER = LoggerFactory.getLogger(NoModleDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List> list = new ArrayList>(); + + @Override + public void invoke(Map data, AnalysisContext context) { + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + list.add(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/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java index 5665432..9ebafa4 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 @@ -217,4 +217,13 @@ public class ReadTest { } } + /** + * 不创建对象的读,不是特别推荐使用,都用String接收对日期的支持不是很好 + */ + @Test + public void noModleRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 只要,然后读取第一个sheet 同步读取会自动finish + EasyExcel.read(fileName, new NoModleDataListener()).sheet().doRead(); + } } diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 336a199..2582f10 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -58,7 +58,6 @@ public class WriteTest { ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build(); WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); excelWriter.write(data(), writeSheet); - // 进行加密 /// 千万别忘记finish 会帮忙关闭流 excelWriter.finish(); } @@ -379,6 +378,17 @@ public class WriteTest { .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); } + /** + * 不创建对象的写 + */ + @Test + public void noModleWrite() { + // 写法1 + String fileName = TestFileUtil.getPath() + "noModleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); + } + private List dataLong() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { @@ -405,6 +415,18 @@ public class WriteTest { return list; } + private List> dataList() { + List> list = new ArrayList>(); + for (int i = 0; i < 10; i++) { + List data = new ArrayList(); + data.add("字符串" + i); + data.add(new Date()); + data.add(0.56); + list.add(data); + } + return list; + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) { From 86f1d2ce6303d5c0b7160a57d613b5aa7d273b65 Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 21 Oct 2019 19:08:25 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99jar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/alibaba/excel/util/WorkBookUtil.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java index 2f39e1c..bcec523 100644 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -1,16 +1,9 @@ package com.alibaba.excel.util; -import java.io.File; import java.io.IOException; -import java.io.OutputStream; import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.openxml4j.opc.PackageAccess; -import org.apache.poi.poifs.crypt.EncryptionInfo; -import org.apache.poi.poifs.crypt.EncryptionMode; -import org.apache.poi.poifs.crypt.Encryptor; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; From a341155c1763686d7471b6e8fdcf479539ae6cec Mon Sep 17 00:00:00 2001 From: Jiaju Zhuang Date: Mon, 21 Oct 2019 19:16:09 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=BF=BD=E7=95=A5stati?= =?UTF-8?q?c?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/metadata/property/ExcelHeadProperty.java | 8 -------- .../easyexcel/test/demo/write/ComplexHeadData.java | 2 -- .../com/alibaba/easyexcel/test/demo/write/WriteTest.java | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) 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 06d94bb..6921161 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -129,14 +129,6 @@ public class ExcelHeadProperty { Map customFiledMap = new TreeMap(); for (Field field : fieldList) { ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class); - if(Modifier.isStatic(field.getModifiers())&&Modifier.isFinal(field.getModifiers())){ - ignoreMap.put(field.getName(),field); - continue; - } - if(Modifier.isTransient(field.getModifiers())){ - ignoreMap.put(field.getName(),field); - continue; - } if (excelIgnore != null) { ignoreMap.put(field.getName(), field); continue; diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java index 4c5d0c6..44ce5cc 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java @@ -13,8 +13,6 @@ import lombok.Data; **/ @Data public class ComplexHeadData { - private final static String TITLE = "title"; - private transient String trans = "transient"; @ExcelProperty({"主标题", "字符串标题"}) private String string; @ExcelProperty({"主标题", "日期标题"}) diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 0da187f..2582f10 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -85,7 +85,7 @@ public class WriteTest { *

* 2. 使用{@link ExcelProperty}注解指定复杂的头 *

- * 3. 直接写即可,写入时会忽略static final和 transient + * 3. 直接写即可 */ @Test public void complexHeadWrite() {