未验证 提交 6dcc85ff 编写于 作者: 庄家钜's avatar 庄家钜 提交者: GitHub

Merge pull request #796 from alibaba/2.1.x

2.1.x
......@@ -62,16 +62,19 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
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)
```java
/**
* 文件下载
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>2. 设置返回的 参数
* <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=demo.xlsx");
// 这里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());
}
......@@ -84,7 +87,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener()).sheet().doRead();
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "success";
}
```
......
......@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.0-beta4</version>
<version>2.1.1</version>
<packaging>jar</packaging>
<name>easyexcel</name>
......
......@@ -14,7 +14,6 @@ import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.cache.MapCache;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.parameter.AnalysisParam;
import com.alibaba.excel.read.listener.ReadListener;
......@@ -35,8 +34,6 @@ public class ExcelReader {
*/
private ExcelAnalyser excelAnalyser;
private boolean finished = false;
/**
* Create new reader
*
......@@ -160,7 +157,6 @@ public class ExcelReader {
* Parse all sheet content by default
*/
public void readAll() {
checkFinished();
excelAnalyser.analysis(null, Boolean.TRUE);
}
......@@ -181,7 +177,6 @@ public class ExcelReader {
* @return
*/
public ExcelReader read(List<ReadSheet> readSheetList) {
checkFinished();
excelAnalyser.analysis(readSheetList, Boolean.FALSE);
return this;
}
......@@ -231,7 +226,6 @@ public class ExcelReader {
* @return
*/
public AnalysisContext analysisContext() {
checkFinished();
return excelAnalyser.analysisContext();
}
......@@ -241,7 +235,6 @@ public class ExcelReader {
* @return
*/
public ExcelReadExecutor excelExecutor() {
checkFinished();
return excelAnalyser.excelExecutor();
}
......@@ -281,10 +274,6 @@ public class ExcelReader {
* Complete the entire read file.Release the cache and close stream.
*/
public void finish() {
if (finished) {
return;
}
finished = true;
excelAnalyser.finish();
}
......@@ -294,19 +283,11 @@ public class ExcelReader {
*/
@Override
protected void finalize() {
if (finished) {
return;
}
try {
excelAnalyser.finish();
finish();
} catch (Throwable e) {
LOGGER.warn("Destroy object failed", e);
}
}
private void checkFinished() {
if (finished) {
throw new ExcelAnalysisException("Can not use a finished reader.");
}
}
}
......@@ -5,6 +5,9 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
......@@ -31,6 +34,8 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
* @author jipengfei
*/
public class ExcelWriter {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelWriter.class);
private ExcelBuilder excelBuilder;
/**
......@@ -320,7 +325,20 @@ public class ExcelWriter {
* Close IO
*/
public void finish() {
excelBuilder.finish();
excelBuilder.finish(false);
}
/**
* Prevents calls to {@link #finish} from freeing the cache
*
*/
@Override
protected void finalize() {
try {
finish();
} catch (Throwable e) {
LOGGER.warn("Destroy object failed", e);
}
}
/**
......
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;
......@@ -37,6 +34,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
private AnalysisContext analysisContext;
private ExcelReadExecutor excelReadExecutor;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public ExcelAnalyserImpl(ReadWorkbook readWorkbook) {
try {
......@@ -124,6 +125,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
@Override
public void finish() {
if (finished) {
return;
}
finished = true;
if (analysisContext == null || analysisContext.readWorkbookHolder() == null) {
return;
}
......
......@@ -3,6 +3,7 @@ package com.alibaba.excel.analysis.v03;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
......@@ -26,6 +27,7 @@ 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;
import com.alibaba.excel.analysis.v03.handlers.IndexRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler;
......@@ -77,7 +79,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) {
this.analysisContext = context;
this.records = new TreeMap<Integer, CellData>();
this.records = new LinkedHashMap<Integer, CellData>();
this.poifsFileSystem = poifsFileSystem;
analysisContext.readWorkbookHolder().setPoifsFileSystem(poifsFileSystem);
}
......@@ -120,7 +122,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
private void init() {
lastRowNumber = 0;
lastColumnNumber = 0;
records = new TreeMap<Integer, CellData>();
records = new LinkedHashMap<Integer, CellData>();
buildXlsRecordHandlers();
}
......@@ -210,6 +212,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
recordHandlers.add(new RkRecordHandler());
recordHandlers.add(new SstRecordHandler());
recordHandlers.add(new MissingCellDummyRecordHandler());
recordHandlers.add(new IndexRecordHandler(analysisContext));
Collections.sort(recordHandlers);
}
......
......@@ -73,7 +73,7 @@ public class BofRecordHandler extends AbstractXlsRecordHandler {
readSheet = SheetUtils.match(readSheet, readSheetList, readAll,
context.readWorkbookHolder().getGlobalConfiguration());
if (readSheet != null) {
if (readSheet.getSheetNo() != 0) {
if (readSheet.getSheetNo() != 0 && context.readSheetHolder() != null) {
// Prompt for the end of the previous form read
context.readSheetHolder().notifyAfterAllAnalysed(context);
}
......
package com.alibaba.excel.analysis.v03.handlers;
import org.apache.poi.hssf.record.IndexRecord;
import org.apache.poi.hssf.record.Record;
import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler;
import com.alibaba.excel.context.AnalysisContext;
/**
* Record handler
*
* @author Jiaju Zhuang
*/
public class IndexRecordHandler extends AbstractXlsRecordHandler {
private AnalysisContext context;
public IndexRecordHandler(AnalysisContext context) {
this.context = context;
}
@Override
public boolean support(Record record) {
return record instanceof IndexRecord;
}
@Override
public void init() {}
@Override
public void processRecord(Record record) {
if (context.readSheetHolder() == null) {
return;
}
context.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1());
}
@Override
public int getOrder() {
return 1;
}
}
......@@ -10,7 +10,7 @@ import com.alibaba.excel.context.AnalysisContext;
/**
* Cell Handler
*
*
* @author jipengfei
*/
public class CountRowCellHandler implements XlsxCellHandler {
......@@ -31,7 +31,7 @@ public class CountRowCellHandler implements XlsxCellHandler {
String d = attributes.getValue(DIMENSION_REF);
String totalStr = d.substring(d.indexOf(":") + 1, d.length());
String c = totalStr.toUpperCase().replaceAll("[A-Z]", "");
analysisContext.readSheetHolder().setTotal(Integer.parseInt(c));
analysisContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c));
}
@Override
......
......@@ -9,9 +9,9 @@ import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TYPE_TAG;
import java.math.BigDecimal;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.xssf.model.StylesTable;
......@@ -37,7 +37,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
private final AnalysisContext analysisContext;
private Deque<String> currentTagDeque = new LinkedList<String>();
private int curCol;
private Map<Integer, CellData> curRowContent = new TreeMap<Integer, CellData>();
private Map<Integer, CellData> curRowContent = new LinkedHashMap<Integer, CellData>();
private CellData currentCellData;
private StringBuilder dataStringBuilder;
private StringBuilder formulaStringBuilder;
......@@ -54,7 +54,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
@Override
public void clearResult() {
curRowContent = new TreeMap<Integer, CellData>();
curRowContent = new LinkedHashMap<Integer, CellData>();
}
@Override
......
......@@ -66,9 +66,10 @@ public interface WriteContext {
/**
* close
*
* @param onException
*/
void finish();
void finish(boolean onException);
/**
* Current sheet
......
......@@ -65,6 +65,10 @@ public class WriteContextImpl implements WriteContext {
* Configuration of currently operated cell
*/
private WriteHolder currentWriteHolder;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public WriteContextImpl(WriteWorkbook writeWorkbook) {
if (writeWorkbook == null) {
......@@ -249,23 +253,36 @@ public class WriteContextImpl implements WriteContext {
}
@Override
public void finish() {
public void finish(boolean onException) {
if (finished) {
return;
}
finished = true;
WriteHandlerUtils.afterWorkbookDispose(this);
if (writeWorkbookHolder == null) {
return;
}
Throwable throwable = null;
boolean isOutputStreamEncrypt = false;
try {
isOutputStreamEncrypt = doOutputStreamEncrypt07();
} catch (Throwable t) {
throwable = t;
// Determine if you need to write excel
boolean writeExcel = !onException;
if (writeWorkbookHolder.getWriteExcelOnException()) {
writeExcel = Boolean.TRUE;
}
// No data is written if an exception is thrown
if (writeExcel) {
try {
isOutputStreamEncrypt = doOutputStreamEncrypt07();
} catch (Throwable t) {
throwable = t;
}
}
if (!isOutputStreamEncrypt) {
try {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
if (writeExcel) {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
}
writeWorkbookHolder.getWorkbook().close();
} catch (Throwable t) {
throwable = t;
......@@ -289,7 +306,7 @@ public class WriteContextImpl implements WriteContext {
throwable = t;
}
if (!isOutputStreamEncrypt) {
if (writeExcel && !isOutputStreamEncrypt) {
try {
doFileEncrypt07();
} catch (Throwable t) {
......
......@@ -37,6 +37,7 @@ import com.alibaba.excel.converters.string.StringBooleanConverter;
import com.alibaba.excel.converters.string.StringErrorConverter;
import com.alibaba.excel.converters.string.StringNumberConverter;
import com.alibaba.excel.converters.string.StringStringConverter;
import com.alibaba.excel.converters.url.UrlImageConverter;
/**
* Load default handler
......@@ -71,6 +72,7 @@ public class DefaultConverterLoader {
putWriteConverter(new InputStreamImageConverter());
putWriteConverter(new ByteArrayImageConverter());
putWriteConverter(new BoxingByteArrayImageConverter());
putWriteConverter(new UrlImageConverter());
return defaultWriteConverter;
}
......
package com.alibaba.excel.converters.url;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
/**
* Url and image converter
*
* @since 2.1.1
* @author Jiaju Zhuang
*/
public class UrlImageConverter implements Converter<URL> {
@Override
public Class supportJavaTypeKey() {
return URL.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.IMAGE;
}
@Override
public URL convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
throw new UnsupportedOperationException("Cannot convert images to url.");
}
@Override
public CellData convertToExcelData(URL value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws IOException {
InputStream inputStream = null;
try {
inputStream = value.openStream();
byte[] bytes = IoUtils.toByteArray(inputStream);
return new CellData(bytes);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
......@@ -16,7 +16,7 @@ public abstract class AnalysisEventListener<T> implements ReadListener<T> {
@Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context);
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
}
/**
......
package com.alibaba.excel.exception;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
......@@ -17,6 +18,10 @@ public class ExcelDataConvertException extends RuntimeException {
* NotNull.
*/
private Integer columnIndex;
/**
* NotNull.
*/
private CellData cellData;
/**
* Nullable.Only when the header is configured and when the class header is used is not null.
*
......@@ -24,34 +29,24 @@ public class ExcelDataConvertException extends RuntimeException {
*/
private ExcelContentProperty excelContentProperty;
public ExcelDataConvertException(String message) {
super(message);
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message) {
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
ExcelContentProperty excelContentProperty, String message) {
super(message);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message, Throwable cause) {
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
ExcelContentProperty excelContentProperty, String message, Throwable cause) {
super(message, cause);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(String message, Throwable cause) {
super(message, cause);
}
public ExcelDataConvertException(Throwable cause) {
super(cause);
}
public Integer getRowIndex() {
return rowIndex;
}
......@@ -75,4 +70,12 @@ public class ExcelDataConvertException extends RuntimeException {
public void setExcelContentProperty(ExcelContentProperty excelContentProperty) {
this.excelContentProperty = excelContentProperty;
}
public CellData getCellData() {
return cellData;
}
public void setCellData(CellData cellData) {
this.cellData = cellData;
}
}
......@@ -229,18 +229,21 @@ public class CellData<T> {
@Override
public String toString() {
if (type == null) {
return "empty";
return StringUtils.EMPTY;
}
switch (type) {
case NUMBER:
return numberValue.toString();
case BOOLEAN:
return booleanValue.toString();
case DIRECT_STRING:
case STRING:
case ERROR:
return stringValue;
case IMAGE:
return "image[" + imageValue.length + "]";
default:
return "empty";
return StringUtils.EMPTY;
}
}
......
......@@ -24,6 +24,7 @@ import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
......@@ -119,51 +120,10 @@ public class ExcelHeadProperty {
if (headClazz == null) {
return;
}
List<Field> fieldList = new ArrayList<Field>();
Class tempClass = headClazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)headClazz.getAnnotation(ExcelIgnoreUnannotated.class);
// Screening of field
// Declared fields
List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
for (Field field : fieldList) {
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
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;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelGenerateException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
}
ClassUtils.declaredFields(headClazz, defaultFieldList, customFiledMap, ignoreMap, convertAllFiled);
int index = 0;
for (Field field : defaultFieldList) {
......
......@@ -166,7 +166,7 @@ public class ExcelReaderSheetBuilder {
*
* @return
*/
public List<Object> doReadSync() {
public <T> List<T> doReadSync() {
if (excelReader == null) {
throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
}
......@@ -174,7 +174,7 @@ public class ExcelReaderSheetBuilder {
registerReadListener(syncReadListener);
excelReader.read(build());
excelReader.finish();
return syncReadListener.getList();
return (List<T>)syncReadListener.getList();
}
}
......@@ -2,6 +2,7 @@ package com.alibaba.excel.read.listener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
......@@ -44,7 +45,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
AnalysisContext context) {
int index = 0;
if (context.readWorkbookHolder().getDefaultReturnMap()) {
Map<Integer, String> map = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
Map<Integer, String> map = new LinkedHashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue();
......@@ -92,7 +93,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
try {
resultModel = excelReadHeadProperty.getHeadClazz().newInstance();
} catch (Exception e) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0,
new CellData(CellDataTypeEnum.EMPTY), null,
"Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);
}
Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();
......
......@@ -190,8 +190,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) {
return;
}
Map<Integer, String> dataMap =
ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder());
Map<Integer, String> dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext);
ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty();
Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap();
Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap();
......@@ -208,6 +207,9 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
List<String> headNameList = headData.getHeadNameList();
String headName = headNameList.get(headNameList.size() - 1);
for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) {
if (stringEntry == null) {
continue;
}
String headString = stringEntry.getValue();
Integer stringKey = stringEntry.getKey();
if (StringUtils.isEmpty(headString)) {
......
......@@ -26,10 +26,9 @@ public class ReadSheetHolder extends AbstractReadHolder {
*/
private String sheetName;
/**
* get total row , Data may be inaccurate
* Gets the total number of rows , data may be inaccurate
*/
@Deprecated
private Integer total;
private Integer approximateTotalRowNumber;
public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) {
super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled());
......@@ -71,12 +70,29 @@ public class ReadSheetHolder extends AbstractReadHolder {
this.sheetName = sheetName;
}
/**
*
* Approximate total number of rows
*
* @return
* @see #getApproximateTotalRowNumber()
*/
@Deprecated
public Integer getTotal() {
return total;
return approximateTotalRowNumber;
}
/**
* Approximate total number of rows
*
* @return
*/
public Integer getApproximateTotalRowNumber() {
return approximateTotalRowNumber;
}
public void setTotal(Integer total) {
this.total = total;
public void setApproximateTotalRowNumber(Integer approximateTotalRowNumber) {
this.approximateTotalRowNumber = approximateTotalRowNumber;
}
@Override
......
package com.alibaba.excel.util;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.metadata.BaseRowModel;
/**
* Class utils
*
* @author Jiaju Zhuang
**/
public class ClassUtils {
private static final Map<Class, SoftReference<FieldCache>> FIELD_CACHE =
new ConcurrentHashMap<Class, SoftReference<FieldCache>>();
public static void declaredFields(Class clazz, List<Field> defaultFieldList, Map<Integer, Field> customFiledMap,
Map<String, Field> ignoreMap, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
defaultFieldList.addAll(fieldCache.getDefaultFieldList());
customFiledMap.putAll(fieldCache.getCustomFiledMap());
ignoreMap.putAll(fieldCache.getIgnoreMap());
}
}
public static void declaredFields(Class clazz, List<Field> fieldList, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
fieldList.addAll(fieldCache.getAllFieldList());
}
}
private static FieldCache getFieldCache(Class clazz, Boolean convertAllFiled) {
if (clazz == null) {
return null;
}
SoftReference<FieldCache> fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
synchronized (clazz) {
fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
declaredFields(clazz, convertAllFiled);
}
return FIELD_CACHE.get(clazz).get();
}
private static void declaredFields(Class clazz, Boolean convertAllFiled) {
List<Field> tempFieldList = new ArrayList<Field>();
Class tempClass = clazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null && tempClass != BaseRowModel.class) {
Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
// Screening of field
List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
List<Field> allFieldList = new ArrayList<Field>();
Map<String, Field> ignoreMap = new HashMap<String, Field>(16);
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)clazz.getAnnotation(ExcelIgnoreUnannotated.class);
for (Field field : tempFieldList) {
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
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);
allFieldList.add(field);
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelCommonException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
allFieldList.add(field);
}
FIELD_CACHE.put(clazz,
new SoftReference<FieldCache>(new FieldCache(defaultFieldList, customFiledMap, allFieldList, ignoreMap)));
}
private static class FieldCache {
private List<Field> defaultFieldList;
private Map<Integer, Field> customFiledMap;
private List<Field> allFieldList;
private Map<String, Field> ignoreMap;
public FieldCache(List<Field> defaultFieldList, Map<Integer, Field> customFiledMap, List<Field> allFieldList,
Map<String, Field> ignoreMap) {
this.defaultFieldList = defaultFieldList;
this.customFiledMap = customFiledMap;
this.allFieldList = allFieldList;
this.ignoreMap = ignoreMap;
}
public List<Field> getDefaultFieldList() {
return defaultFieldList;
}
public Map<Integer, Field> getCustomFiledMap() {
return customFiledMap;
}
public List<Field> getAllFieldList() {
return allFieldList;
}
public Map<String, Field> getIgnoreMap() {
return ignoreMap;
}
}
}
......@@ -6,6 +6,7 @@ import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum;
......@@ -28,28 +29,37 @@ public class ConverterUtils {
* Convert it into a String map
*
* @param cellDataMap
* @param readHolder
* @param context
* @return
*/
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, ReadHolder readHolder) {
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, AnalysisContext context) {
Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
ReadHolder currentReadHolder = context.currentReadHolder();
int index = 0;
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue();
while (index < key) {
stringMap.put(index, null);
index++;
}
index++;
if (cellData.getType() == CellDataTypeEnum.EMPTY) {
stringMap.put(entry.getKey(), null);
stringMap.put(key, null);
continue;
}
Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
currentReadHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Converter not found, convert " + cellData.getType() + " to String");
}
try {
stringMap.put(entry.getKey(),
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration())));
stringMap.put(key,
(String)(converter.convertToJavaData(cellData, null, currentReadHolder.globalConfiguration())));
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to String error ", e);
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Convert data " + cellData + " to String error ", e);
}
}
return stringMap;
......@@ -116,13 +126,13 @@ public class ConverterUtils {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
}
if (converter == null) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
}
try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Convert data " + cellData + " to " + clazz + " error ", e);
}
}
......
......@@ -12,6 +12,7 @@ import com.alibaba.excel.exception.ExcelDataConvertException;
* @author Jiaju Zhuang
**/
public class DateUtils {
public static final String DATE_FORMAT_10 = "yyyy-MM-dd";
public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss";
public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss";
public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss";
......@@ -65,8 +66,10 @@ public class DateUtils {
return DATE_FORMAT_17;
case 14:
return DATE_FORMAT_14;
case 10:
return DATE_FORMAT_10;
default:
throw new ExcelDataConvertException("can not find date format for:" + dateString);
throw new IllegalArgumentException("can not find date format for:" + dateString);
}
}
......
......@@ -71,11 +71,14 @@ public interface ExcelBuilder {
/**
* Close io
*
* @param onException
*/
void finish();
void finish(boolean onException);
/**
* add password
*
* @param data
* @param writeSheet
* @param writeTable
......
......@@ -31,10 +31,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
FileUtils.createPoiFilesDirectory();
context = new WriteContextImpl(writeWorkbook);
} catch (RuntimeException e) {
finish();
finishOnException();
throw e;
} catch (Throwable e) {
finish();
finishOnException();
throw new ExcelGenerateException(e);
}
}
......@@ -57,10 +57,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
}
excelWriteAddExecutor.add(data);
} catch (RuntimeException e) {
finish();
finishOnException();
throw e;
} catch (Throwable e) {
finish();
finishOnException();
throw new ExcelGenerateException(e);
}
}
......@@ -80,18 +80,22 @@ public class ExcelBuilderImpl implements ExcelBuilder {
}
excelWriteFillExecutor.fill(data, fillConfig);
} catch (RuntimeException e) {
finish();
finishOnException();
throw e;
} catch (Throwable e) {
finish();
finishOnException();
throw new ExcelGenerateException(e);
}
}
private void finishOnException() {
finish(true);
}
@Override
public void finish() {
public void finish(boolean onException) {
if (context != null) {
context.finish();
context.finish(onException);
}
}
......@@ -108,10 +112,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
}
excelWriteAddExecutor.add(data);
} catch (RuntimeException e) {
finish();
finishOnException();
throw e;
} catch (Throwable e) {
finish();
finishOnException();
throw new ExcelGenerateException(e);
}
}
......
......@@ -146,6 +146,14 @@ public class ExcelWriterBuilder {
return this;
}
/**
* Excel is also written in the event of an exception being thrown.The default false.
*/
public ExcelWriterBuilder writeExcelOnException(Boolean writeExcelOnException) {
writeWorkbook.setWriteExcelOnException(writeExcelOnException);
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.
......
......@@ -59,8 +59,9 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
case EMPTY:
return cellData;
default:
throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType()
+ "at row:" + cell.getRow().getRowNum());
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(), cellData,
excelContentProperty, "Not supported data:" + value + " return type:" + cell.getCellType()
+ "at row:" + cell.getRow().getRowNum());
}
}
......@@ -102,7 +103,8 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz));
}
if (converter == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Can not find 'Converter' support class " + clazz.getSimpleName() + ".");
}
CellData cellData;
......@@ -110,11 +112,13 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
cellData =
converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration());
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(),
e);
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), e);
}
if (cellData == null || cellData.getType() == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum());
}
return cellData;
......
......@@ -2,7 +2,6 @@ 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;
......@@ -13,10 +12,10 @@ 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.ClassUtils;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.WorkBookUtil;
import com.alibaba.excel.util.WriteHandlerUtils;
......@@ -158,13 +157,11 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
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);
CellData cellData =
converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell, value, null);
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
}
}
......@@ -173,13 +170,8 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
if (!fieldList.isEmpty()) {
return;
}
Class tempClass = clazz;
while (tempClass != null) {
if (tempClass != BaseRowModel.class) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
}
tempClass = tempClass.getSuperclass();
}
ClassUtils.declaredFields(clazz, fieldList,
writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled());
}
}
......@@ -3,9 +3,11 @@ package com.alibaba.excel.write.executor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
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.CellStyle;
......@@ -57,6 +59,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
*/
private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache =
new HashMap<Integer, Map<AnalysisCell, CellStyle>>(8);
/**
* Row height cache for collection
*/
private Map<Integer, Short> collectionRowHeightCache = new HashMap<Integer, Short>(8);
/**
* Last index cache for collection fields
*/
......@@ -121,7 +127,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (collectionLastIndexMap == null) {
number--;
}
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number);
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false);
for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) {
if (analysisCell.getRowIndex() > maxRowIndex) {
analysisCell.setRowIndex(analysisCell.getRowIndex() + number);
......@@ -245,7 +251,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} else {
row = sheet.createRow(lastRowIndex);
}
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE);
} else {
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
}
}
Cell cell = row.getCell(lastColumnIndex);
......@@ -271,6 +280,21 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
return cell;
}
private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row,
Integer sheetNo) {
if (!analysisCell.getFirstColumn() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) {
return;
}
if (isOriginalCell) {
collectionRowHeightCache.put(sheetNo, row.getHeight());
return;
}
Short rowHeight = collectionRowHeightCache.get(sheetNo);
if (rowHeight != null) {
row.setHeight(rowHeight);
}
}
private List<AnalysisCell> readTemplateData(Map<Integer, List<AnalysisCell>> analysisCache) {
Integer sheetNo = writeContext.writeSheetHolder().getSheetNo();
List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo);
......@@ -280,6 +304,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
Sheet sheet = writeContext.writeSheetHolder().getCachedSheet();
analysisCellList = new ArrayList<AnalysisCell>();
List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>();
Set<Integer> firstColumnCache = new HashSet<Integer>();
for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) {
......@@ -290,7 +315,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (cell == null) {
continue;
}
String preparedData = prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j);
String preparedData =
prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j, firstColumnCache);
// Prevent empty data from not being replaced
if (preparedData != null) {
cell.setCellValue(preparedData);
......@@ -310,10 +336,11 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
* @param collectionAnalysisCellList
* @param rowIndex
* @param columnIndex
* @param firstColumnCache
* @return Returns the data that the cell needs to replace
*/
private String prepareData(Cell cell, List<AnalysisCell> analysisCellList,
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex) {
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex, Set<Integer> firstColumnCache) {
if (!CellType.STRING.equals(cell.getCellTypeEnum())) {
return null;
}
......@@ -323,6 +350,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
}
StringBuilder preparedData = new StringBuilder();
AnalysisCell analysisCell = null;
int startIndex = 0;
int length = value.length();
int lastPrepareDataIndex = 0;
......@@ -376,6 +404,13 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
}
lastPrepareDataIndex = suffixIndex + 1;
}
return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, analysisCellList,
collectionAnalysisCellList, firstColumnCache, preparedData);
}
private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex,
int length, List<AnalysisCell> analysisCellList, List<AnalysisCell> collectionAnalysisCellList,
Set<Integer> firstColumnCache, StringBuilder preparedData) {
if (analysisCell != null) {
if (lastPrepareDataIndex == length) {
analysisCell.getPrepareDataList().add(StringUtils.EMPTY);
......@@ -386,6 +421,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) {
analysisCellList.add(analysisCell);
} else {
if (!firstColumnCache.contains(rowIndex)) {
analysisCell.setFirstColumn(Boolean.TRUE);
firstColumnCache.add(rowIndex);
}
collectionAnalysisCellList.add(analysisCell);
}
return preparedData.toString();
......@@ -403,6 +442,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
List<String> prepareDataList = new ArrayList<String>();
analysisCell.setPrepareDataList(prepareDataList);
analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON);
analysisCell.setFirstColumn(Boolean.FALSE);
return analysisCell;
}
......
......@@ -13,24 +13,39 @@ import com.alibaba.excel.metadata.Head;
*/
public class LoopMergeStrategy extends AbstractMergeStrategy {
private int eachRow;
private int columnCount;
private int columnIndex;
public LoopMergeStrategy(int eachRow, int columnIndex) {
this(eachRow, 1, columnIndex);
}
public LoopMergeStrategy(int eachRow, int columnCount, int columnIndex) {
if (eachRow < 1) {
throw new IllegalArgumentException("EachRows must be greater than 1");
}
if (columnCount < 1) {
throw new IllegalArgumentException("ColumnCount must be greater than 1");
}
if (columnCount == 1 && eachRow == 1) {
throw new IllegalArgumentException("ColumnCount or eachRows must be greater than 1");
}
if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0");
}
this.eachRow = eachRow;
this.columnCount = columnCount;
this.columnIndex = columnIndex;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(),
cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex());
CellRangeAddress cellRangeAddress = new CellRangeAddress(
cell.getRowIndex(),
cell.getRowIndex() + eachRow - 1,
cell.getColumnIndex(),
cell.getColumnIndex() + columnCount - 1);
sheet.addMergedRegion(cellRangeAddress);
}
}
......
......@@ -63,6 +63,10 @@ public class WriteWorkbook extends WriteBasicParameter {
* Comment and RichTextString are only supported in memory mode.
*/
private Boolean inMemory;
/**
* Excel is also written in the event of an exception being thrown.The default false.
*/
private Boolean writeExcelOnException;
/**
* The default is all excel objects.Default is true.
* <p>
......@@ -169,4 +173,12 @@ public class WriteWorkbook extends WriteBasicParameter {
public void setInMemory(Boolean inMemory) {
this.inMemory = inMemory;
}
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
}
......@@ -16,6 +16,7 @@ public class AnalysisCell {
private List<String> prepareDataList;
private Boolean onlyOneVariable;
private WriteTemplateAnalysisCellTypeEnum cellType;
private Boolean firstColumn;
public int getColumnIndex() {
return columnIndex;
......@@ -65,6 +66,14 @@ public class AnalysisCell {
this.cellType = cellType;
}
public Boolean getFirstColumn() {
return firstColumn;
}
public void setFirstColumn(Boolean firstColumn) {
this.firstColumn = firstColumn;
}
@Override
public boolean equals(Object o) {
if (this == o) {
......
......@@ -103,6 +103,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
* Comment and RichTextString are only supported in memory mode.
*/
private Boolean inMemory;
/**
* Excel is also written in the event of an exception being thrown.The default false.
*/
private Boolean writeExcelOnException;
public WriteWorkbookHolder(WriteWorkbook writeWorkbook) {
super(writeWorkbook, null, writeWorkbook.getConvertAllFiled());
......@@ -128,7 +132,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
throw new ExcelGenerateException("Copy template failure.", e);
}
if (writeWorkbook.getExcelType() == null) {
if (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) {
boolean isXls = (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue()))
|| (writeWorkbook.getTemplateFile() != null
&& writeWorkbook.getTemplateFile().getName().endsWith(ExcelTypeEnum.XLS.getValue()));
if (isXls) {
this.excelType = ExcelTypeEnum.XLS;
} else {
this.excelType = ExcelTypeEnum.XLSX;
......@@ -148,6 +155,11 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
} else {
this.inMemory = writeWorkbook.getInMemory();
}
if (writeWorkbook.getWriteExcelOnException() == null) {
this.writeExcelOnException = Boolean.FALSE;
} else {
this.writeExcelOnException = writeWorkbook.getWriteExcelOnException();
}
}
private void copyTemplate() throws IOException {
......@@ -281,6 +293,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
this.inMemory = inMemory;
}
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
@Override
public HolderEnum holderType() {
return HolderEnum.WORKBOOK;
......
......@@ -19,12 +19,15 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
/**
* 写的填充写法
*
* @since 2.1.1
* @author Jiaju Zhuang
*/
@Ignore
public class FillTest {
/**
* 最简单的填充
*
* @since 2.1.1
*/
@Test
public void simpleFill() {
......@@ -51,6 +54,8 @@ public class FillTest {
/**
* 填充列表
*
* @since 2.1.1
*/
@Test
public void listFill() {
......@@ -76,6 +81,8 @@ public class FillTest {
/**
* 复杂的填充
*
* @since 2.1.1
*/
@Test
public void complexFill() {
......@@ -105,6 +112,8 @@ public class FillTest {
* 数据量大的复杂填充
* <p>
* 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。
*
* @since 2.1.1
*/
@Test
public void complexFillWithTable() {
......@@ -145,6 +154,8 @@ public class FillTest {
/**
* 横向的填充
*
* @since 2.1.1
*/
@Test
public void horizontalFill() {
......
package com.alibaba.easyexcel.test.demo.read;
import java.util.List;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
*
* @author Jiaju Zhuang
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
......@@ -23,19 +23,52 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/
private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
......@@ -45,6 +78,7 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
......@@ -39,8 +39,8 @@ public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoDa
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
}
}
......
......@@ -38,8 +38,8 @@ public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
}
}
......
......@@ -202,17 +202,15 @@ public class ReadTest {
public void synchronousRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
List<Object> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (Object obj : list) {
DemoData data = (DemoData)obj;
List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (DemoData data : list) {
LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
}
// 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
list = EasyExcel.read(fileName).sheet().doReadSync();
for (Object obj : list) {
List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
for (Map<Integer, String> data : listMap) {
// 返回每条数据的键值对 表示所在的列 和所在列的值
Map<Integer, String> data = (Map<Integer, String>)obj;
LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
}
}
......
package com.alibaba.easyexcel.test.demo.web;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.alibaba.easyexcel.test.demo.read.DemoData;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
*
* @author Jiaju Zhuang
**/
@Repository
public class UploadDAO {
public void save(List<UploadData> list) {
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
......@@ -15,26 +15,61 @@ import com.alibaba.fastjson.JSON;
*
* @author Jiaju Zhuang
*/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class UploadDataListener extends AnalysisEventListener<UploadData> {
private static final Logger LOGGER = LoggerFactory.getLogger(UploadDataListener.class);
private static final Logger LOGGER =
LoggerFactory.getLogger(com.alibaba.easyexcel.test.demo.read.DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<UploadData> list = new ArrayList<UploadData>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private UploadDAO uploadDAO;
public UploadDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
uploadDAO = new UploadDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param uploadDAO
*/
public UploadDataListener(UploadDAO uploadDAO) {
this.uploadDAO = uploadDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(UploadData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
......@@ -44,6 +79,7 @@ public class UploadDataListener extends AnalysisEventListener<UploadData> {
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
uploadDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
......@@ -2,14 +2,15 @@ package com.alibaba.easyexcel.test.demo.web;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
......@@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
/**
* web读写案例
......@@ -25,8 +27,12 @@ import com.alibaba.excel.EasyExcel;
**/
@Controller
public class WebTest {
@Autowired
private UploadDAO uploadDAO;
/**
* 文件下载
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>
......@@ -45,6 +51,35 @@ public class WebTest {
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
/**
* 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
*
* @since 2.1.1
*/
@GetMapping("downloadFailedUsingJson")
public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
try {
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).autoCloseStream(Boolean.FALSE).sheet("模板")
.doWrite(data());
} catch (Exception e) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
/**
* 文件上传
* <p>
......@@ -57,7 +92,7 @@ public class WebTest {
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener()).sheet().doRead();
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "success";
}
......
......@@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.demo.write;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
......@@ -27,4 +28,10 @@ public class ImageData {
@ExcelProperty(converter = StringImageConverter.class)
private String string;
private byte[] byteArray;
/**
* 根据url导出
*
* @since 2.1.1
*/
private URL url;
}
......@@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.demo.write;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
......@@ -215,12 +216,14 @@ public class WriteTest {
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);
imageData.setUrl(new URL(
"https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));
EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);
} finally {
if (inputStream != null) {
......
package com.alibaba.easyexcel.test.temp;
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.demo.fill.FillData;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
/**
* 写的填充写法
*
* @since 2.1.1
* @author Jiaju Zhuang
*/
@Ignore
public class FillTempTest {
/**
* 复杂的填充
*
* @since 2.1.1
*/
@Test
public void complexFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
String templateFileName = "D:\\test\\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<String, Object> map = new HashMap<String, Object>();
map.put("date", "2019年10月9日13:28:28");
map.put("total", 1000);
excelWriter.fill(map, writeSheet);
excelWriter.finish();
}
/**
* 数据量大的复杂填充
* <p>
* 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。
*
* @since 2.1.1
*/
@Test
public void complexFillWithTable() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
// 这里模板 删除了list以后的数据,也就是统计的这一行
String templateFileName = "D:\\test\\complex.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<String, Object> map = new HashMap<String, Object>();
map.put("date", "2019年10月9日13:28:28");
excelWriter.fill(map, writeSheet);
// list 后面还有个统计 想办法手动写入
// 这里偷懒直接用list 也可以用对象
List<List<String>> totalListList = new ArrayList<List<String>>();
List<String> totalList = new ArrayList<String>();
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需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案
}
private List<FillData> data() {
List<FillData> list = new ArrayList<FillData>();
for (int i = 0; i < 10; i++) {
FillData fillData = new FillData();
list.add(fillData);
fillData.setName("张三");
fillData.setNumber(5.2);
}
return list;
}
}
......@@ -79,11 +79,11 @@ public class PoiTest {
@Test
public void lastRowNum255() throws IOException, InvalidFormatException {
String file = TestFileUtil.getPath() + "fill" + File.separator + "complex.xlsx";
String file = "D:\\test\\complex.xlsx";
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file));
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook);
Sheet xssfSheet = xssfWorkbook.getSheetAt(0);
xssfSheet.shiftRows(2, 4, 10);
xssfSheet.shiftRows(1, 4, 10, true, true);
FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx");
sxssfWorkbook.write(fileout);
......
package com.alibaba.easyexcel.test.temp.read;
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 HDListener extends AnalysisEventListener<HeadReadData> {
private static final Logger LOGGER = LoggerFactory.getLogger(HDListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("HEAD:{}", JSON.toJSONString(headMap));
LOGGER.info("total:{}", context.readSheetHolder().getTotal());
}
@Override
public void invoke(HeadReadData data, AnalysisContext context) {
LOGGER.info("index:{}", context.readRowHolder().getRowIndex());
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
LOGGER.info("所有数据解析完成!");
}
}
package com.alibaba.easyexcel.test.temp.read;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 临时测试
*
* @author Jiaju Zhuang
**/
@Data
@Accessors(chain = true)
public class HeadReadData {
@ExcelProperty("头1")
private String h1;
@ExcelProperty({"头", "头2"})
private String h2;
}
package com.alibaba.easyexcel.test.temp.read;
import java.io.File;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.EasyExcel;
/**
* 临时测试
*
* @author Jiaju Zhuang
**/
@Ignore
public class HeadReadTest {
private static final Logger LOGGER = LoggerFactory.getLogger(HeadReadTest.class);
@Test
public void test() throws Exception {
File file = new File("D:\\test\\headt1.xls");
EasyExcel.read(file, HeadReadData.class, new HDListener()).sheet(0).doRead();
}
}
# 2.1.1
* 发布正式版
* 修改map返回为LinkedHashMap
* 修改同步读取返回对象支持泛型
* 修复03版不能直接读取第二个sheet的bug [Issue #772](https://github.com/alibaba/easyexcel/issues/772)
* 新增支持图片导出用URL [Issue #774](https://github.com/alibaba/easyexcel/issues/774)
* 加入多次关闭判断,防止多次关闭异常
* 加入根据模板自动识别导出的excel类型
* 修改默认失败后,不再往文件流写入数据。通过参数`writeExcelOnException` 参数设置异常了也要写入前面的数据。
* 循环合并策略支持一次性合并多列
* `ExcelDataConvertException`返回新增具体报错的数据
* 加入解析class缓存
* 修复填充的时候行高不复制的Bug [Issue #780](https://github.com/alibaba/easyexcel/issues/780)
* 修复03版无法获取大概总行数的bug
# 2.1.0-beta4
* 修改最长匹配策略会空指针的bug [Issue #747](https://github.com/alibaba/easyexcel/issues/747)
* 修改afterRowDispose错误 [Issue #751](https://github.com/alibaba/easyexcel/issues/751)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册