Browse Source

Merge pull request #796 from alibaba/2.1.x

2.1.x
developing v2.1.1
Jiaju Zhuang 5 years ago committed by GitHub
parent
commit
6dcc85ff45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      README.md
  2. BIN
      img/readme/quickstart/fill/complexFill.png
  3. BIN
      img/readme/quickstart/fill/complexFillTemplate.png
  4. BIN
      img/readme/quickstart/fill/complexFillWithTable.png
  5. BIN
      img/readme/quickstart/fill/complexFillWithTableTemplate.png
  6. BIN
      img/readme/quickstart/fill/horizontalFill.png
  7. BIN
      img/readme/quickstart/fill/horizontalFillTemplate.png
  8. BIN
      img/readme/quickstart/fill/listFill.png
  9. BIN
      img/readme/quickstart/fill/listFillTemplate.png
  10. BIN
      img/readme/quickstart/fill/simpleFill.png
  11. BIN
      img/readme/quickstart/fill/simpleFillTemplate.png
  12. BIN
      img/readme/quickstart/read/demo.png
  13. BIN
      img/readme/quickstart/write/complexHeadWrite.png
  14. BIN
      img/readme/quickstart/write/converterWrite.png
  15. BIN
      img/readme/quickstart/write/customHandlerWrite.png
  16. BIN
      img/readme/quickstart/write/dynamicHeadWrite.png
  17. BIN
      img/readme/quickstart/write/imageWrite.png
  18. BIN
      img/readme/quickstart/write/indexWrite.png
  19. BIN
      img/readme/quickstart/write/longestMatchColumnWidthWrite.png
  20. BIN
      img/readme/quickstart/write/mergeWrite.png
  21. BIN
      img/readme/quickstart/write/repeatedWrite.png
  22. BIN
      img/readme/quickstart/write/simpleWrite.png
  23. BIN
      img/readme/quickstart/write/styleWrite.png
  24. BIN
      img/readme/quickstart/write/tableWrite.png
  25. BIN
      img/readme/quickstart/write/templateWrite.png
  26. BIN
      img/readme/quickstart/write/widthAndHeightWrite.png
  27. BIN
      img/readme/wechat.png
  28. 2
      pom.xml
  29. 21
      src/main/java/com/alibaba/excel/ExcelReader.java
  30. 20
      src/main/java/com/alibaba/excel/ExcelWriter.java
  31. 11
      src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java
  32. 7
      src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java
  33. 2
      src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java
  34. 42
      src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java
  35. 4
      src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java
  36. 6
      src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java
  37. 5
      src/main/java/com/alibaba/excel/context/WriteContext.java
  38. 33
      src/main/java/com/alibaba/excel/context/WriteContextImpl.java
  39. 2
      src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java
  40. 52
      src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java
  41. 2
      src/main/java/com/alibaba/excel/event/AnalysisEventListener.java
  42. 35
      src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java
  43. 7
      src/main/java/com/alibaba/excel/metadata/CellData.java
  44. 46
      src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  45. 4
      src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java
  46. 6
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  47. 6
      src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java
  48. 28
      src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java
  49. 150
      src/main/java/com/alibaba/excel/util/ClassUtils.java
  50. 30
      src/main/java/com/alibaba/excel/util/ConverterUtils.java
  51. 5
      src/main/java/com/alibaba/excel/util/DateUtils.java
  52. 5
      src/main/java/com/alibaba/excel/write/ExcelBuilder.java
  53. 24
      src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java
  54. 8
      src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java
  55. 16
      src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java
  56. 18
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java
  57. 46
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java
  58. 19
      src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java
  59. 12
      src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java
  60. 9
      src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java
  61. 22
      src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java
  62. 11
      src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java
  63. 15
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDAO.java
  64. 34
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java
  65. 4
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java
  66. 4
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java
  67. 10
      src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java
  68. 20
      src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDAO.java
  69. 38
      src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java
  70. 43
      src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java
  71. 7
      src/test/java/com/alibaba/easyexcel/test/demo/write/ImageData.java
  72. 5
      src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java
  73. 110
      src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java
  74. 4
      src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java
  75. 43
      src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java
  76. 20
      src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java
  77. 28
      src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java
  78. 15
      update.md

9
README.md

@ -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) 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 ```java
/** /**
* 文件下载 * 文件下载(失败了会返回一个有部分数据的Excel)
* <p>1. 创建excel对应的实体对象 参照{@link DownloadData} * <p>1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>2. 设置返回的 参数 * <p>2. 设置返回的 参数
* <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 * <p>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/ */
@GetMapping("download") @GetMapping("download")
public void download(HttpServletResponse response) throws IOException { public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8"); 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()); 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") @PostMapping("upload")
@ResponseBody @ResponseBody
public String upload(MultipartFile file) throws IOException { 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"; return "success";
} }
``` ```

BIN
img/readme/quickstart/fill/complexFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/readme/quickstart/fill/complexFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
img/readme/quickstart/fill/complexFillWithTable.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

BIN
img/readme/quickstart/fill/complexFillWithTableTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
img/readme/quickstart/fill/horizontalFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/fill/horizontalFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
img/readme/quickstart/fill/listFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

BIN
img/readme/quickstart/fill/listFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

BIN
img/readme/quickstart/fill/simpleFill.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

BIN
img/readme/quickstart/fill/simpleFillTemplate.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
img/readme/quickstart/read/demo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

BIN
img/readme/quickstart/write/complexHeadWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

BIN
img/readme/quickstart/write/converterWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
img/readme/quickstart/write/customHandlerWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

BIN
img/readme/quickstart/write/dynamicHeadWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/write/imageWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

BIN
img/readme/quickstart/write/indexWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

BIN
img/readme/quickstart/write/longestMatchColumnWidthWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

BIN
img/readme/quickstart/write/mergeWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

BIN
img/readme/quickstart/write/repeatedWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

BIN
img/readme/quickstart/write/simpleWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

BIN
img/readme/quickstart/write/styleWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

BIN
img/readme/quickstart/write/tableWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

BIN
img/readme/quickstart/write/templateWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

BIN
img/readme/quickstart/write/widthAndHeightWrite.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

BIN
img/readme/wechat.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

2
pom.xml

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

21
src/main/java/com/alibaba/excel/ExcelReader.java

@ -14,7 +14,6 @@ import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.cache.MapCache; import com.alibaba.excel.cache.MapCache;
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.parameter.AnalysisParam; import com.alibaba.excel.parameter.AnalysisParam;
import com.alibaba.excel.read.listener.ReadListener; import com.alibaba.excel.read.listener.ReadListener;
@ -35,8 +34,6 @@ public class ExcelReader {
*/ */
private ExcelAnalyser excelAnalyser; private ExcelAnalyser excelAnalyser;
private boolean finished = false;
/** /**
* Create new reader * Create new reader
* *
@ -160,7 +157,6 @@ public class ExcelReader {
* Parse all sheet content by default * Parse all sheet content by default
*/ */
public void readAll() { public void readAll() {
checkFinished();
excelAnalyser.analysis(null, Boolean.TRUE); excelAnalyser.analysis(null, Boolean.TRUE);
} }
@ -181,7 +177,6 @@ public class ExcelReader {
* @return * @return
*/ */
public ExcelReader read(List<ReadSheet> readSheetList) { public ExcelReader read(List<ReadSheet> readSheetList) {
checkFinished();
excelAnalyser.analysis(readSheetList, Boolean.FALSE); excelAnalyser.analysis(readSheetList, Boolean.FALSE);
return this; return this;
} }
@ -231,7 +226,6 @@ public class ExcelReader {
* @return * @return
*/ */
public AnalysisContext analysisContext() { public AnalysisContext analysisContext() {
checkFinished();
return excelAnalyser.analysisContext(); return excelAnalyser.analysisContext();
} }
@ -241,7 +235,6 @@ public class ExcelReader {
* @return * @return
*/ */
public ExcelReadExecutor excelExecutor() { public ExcelReadExecutor excelExecutor() {
checkFinished();
return excelAnalyser.excelExecutor(); return excelAnalyser.excelExecutor();
} }
@ -281,10 +274,6 @@ public class ExcelReader {
* Complete the entire read file.Release the cache and close stream. * Complete the entire read file.Release the cache and close stream.
*/ */
public void finish() { public void finish() {
if (finished) {
return;
}
finished = true;
excelAnalyser.finish(); excelAnalyser.finish();
} }
@ -294,19 +283,11 @@ public class ExcelReader {
*/ */
@Override @Override
protected void finalize() { protected void finalize() {
if (finished) {
return;
}
try { try {
excelAnalyser.finish(); finish();
} catch (Throwable e) { } catch (Throwable e) {
LOGGER.warn("Destroy object failed", e); LOGGER.warn("Destroy object failed", e);
} }
} }
private void checkFinished() {
if (finished) {
throw new ExcelAnalysisException("Can not use a finished reader.");
}
}
} }

20
src/main/java/com/alibaba/excel/ExcelWriter.java

@ -5,6 +5,9 @@ import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.WriteContext; import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table; import com.alibaba.excel.metadata.Table;
@ -31,6 +34,8 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
* @author jipengfei * @author jipengfei
*/ */
public class ExcelWriter { public class ExcelWriter {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelWriter.class);
private ExcelBuilder excelBuilder; private ExcelBuilder excelBuilder;
/** /**
@ -320,7 +325,20 @@ public class ExcelWriter {
* Close IO * Close IO
*/ */
public void finish() { 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);
}
} }
/** /**

11
src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java

@ -1,13 +1,10 @@
package com.alibaba.excel.analysis; package com.alibaba.excel.analysis;
import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; 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.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; import org.apache.poi.poifs.filesystem.DocumentFactoryHelper;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
@ -37,6 +34,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
private AnalysisContext analysisContext; private AnalysisContext analysisContext;
private ExcelReadExecutor excelReadExecutor; private ExcelReadExecutor excelReadExecutor;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { public ExcelAnalyserImpl(ReadWorkbook readWorkbook) {
try { try {
@ -124,6 +125,10 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
@Override @Override
public void finish() { public void finish() {
if (finished) {
return;
}
finished = true;
if (analysisContext == null || analysisContext.readWorkbookHolder() == null) { if (analysisContext == null || analysisContext.readWorkbookHolder() == null) {
return; return;
} }

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

@ -3,6 +3,7 @@ package com.alibaba.excel.analysis.v03;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; 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.BlankOrErrorRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; 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.LabelRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler; import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler; import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler;
@ -77,7 +79,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) { public XlsSaxAnalyser(AnalysisContext context, POIFSFileSystem poifsFileSystem) {
this.analysisContext = context; this.analysisContext = context;
this.records = new TreeMap<Integer, CellData>(); this.records = new LinkedHashMap<Integer, CellData>();
this.poifsFileSystem = poifsFileSystem; this.poifsFileSystem = poifsFileSystem;
analysisContext.readWorkbookHolder().setPoifsFileSystem(poifsFileSystem); analysisContext.readWorkbookHolder().setPoifsFileSystem(poifsFileSystem);
} }
@ -120,7 +122,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
private void init() { private void init() {
lastRowNumber = 0; lastRowNumber = 0;
lastColumnNumber = 0; lastColumnNumber = 0;
records = new TreeMap<Integer, CellData>(); records = new LinkedHashMap<Integer, CellData>();
buildXlsRecordHandlers(); buildXlsRecordHandlers();
} }
@ -210,6 +212,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
recordHandlers.add(new RkRecordHandler()); recordHandlers.add(new RkRecordHandler());
recordHandlers.add(new SstRecordHandler()); recordHandlers.add(new SstRecordHandler());
recordHandlers.add(new MissingCellDummyRecordHandler()); recordHandlers.add(new MissingCellDummyRecordHandler());
recordHandlers.add(new IndexRecordHandler(analysisContext));
Collections.sort(recordHandlers); Collections.sort(recordHandlers);
} }

2
src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java

@ -73,7 +73,7 @@ public class BofRecordHandler extends AbstractXlsRecordHandler {
readSheet = SheetUtils.match(readSheet, readSheetList, readAll, readSheet = SheetUtils.match(readSheet, readSheetList, readAll,
context.readWorkbookHolder().getGlobalConfiguration()); context.readWorkbookHolder().getGlobalConfiguration());
if (readSheet != null) { if (readSheet != null) {
if (readSheet.getSheetNo() != 0) { if (readSheet.getSheetNo() != 0 && context.readSheetHolder() != null) {
// Prompt for the end of the previous form read // Prompt for the end of the previous form read
context.readSheetHolder().notifyAfterAllAnalysed(context); context.readSheetHolder().notifyAfterAllAnalysed(context);
} }

42
src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java

@ -0,0 +1,42 @@
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;
}
}

4
src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java

@ -10,7 +10,7 @@ import com.alibaba.excel.context.AnalysisContext;
/** /**
* Cell Handler * Cell Handler
* *
* @author jipengfei * @author jipengfei
*/ */
public class CountRowCellHandler implements XlsxCellHandler { public class CountRowCellHandler implements XlsxCellHandler {
@ -31,7 +31,7 @@ public class CountRowCellHandler implements XlsxCellHandler {
String d = attributes.getValue(DIMENSION_REF); String d = attributes.getValue(DIMENSION_REF);
String totalStr = d.substring(d.indexOf(":") + 1, d.length()); String totalStr = d.substring(d.indexOf(":") + 1, d.length());
String c = totalStr.toUpperCase().replaceAll("[A-Z]", ""); String c = totalStr.toUpperCase().replaceAll("[A-Z]", "");
analysisContext.readSheetHolder().setTotal(Integer.parseInt(c)); analysisContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c));
} }
@Override @Override

6
src/main/java/com/alibaba/excel/analysis/v07/handlers/DefaultCellHandler.java

@ -9,9 +9,9 @@ import static com.alibaba.excel.constant.ExcelXmlConstants.CELL_VALUE_TYPE_TAG;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Deque; import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.TreeMap;
import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.StylesTable;
@ -37,7 +37,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
private final AnalysisContext analysisContext; private final AnalysisContext analysisContext;
private Deque<String> currentTagDeque = new LinkedList<String>(); private Deque<String> currentTagDeque = new LinkedList<String>();
private int curCol; 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 CellData currentCellData;
private StringBuilder dataStringBuilder; private StringBuilder dataStringBuilder;
private StringBuilder formulaStringBuilder; private StringBuilder formulaStringBuilder;
@ -54,7 +54,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
@Override @Override
public void clearResult() { public void clearResult() {
curRowContent = new TreeMap<Integer, CellData>(); curRowContent = new LinkedHashMap<Integer, CellData>();
} }
@Override @Override

5
src/main/java/com/alibaba/excel/context/WriteContext.java

@ -66,9 +66,10 @@ public interface WriteContext {
/** /**
* close * close
*
* @param onException
*/ */
void finish(); void finish(boolean onException);
/** /**
* Current sheet * Current sheet

33
src/main/java/com/alibaba/excel/context/WriteContextImpl.java

@ -65,6 +65,10 @@ public class WriteContextImpl implements WriteContext {
* Configuration of currently operated cell * Configuration of currently operated cell
*/ */
private WriteHolder currentWriteHolder; private WriteHolder currentWriteHolder;
/**
* Prevent multiple shutdowns
*/
private boolean finished = false;
public WriteContextImpl(WriteWorkbook writeWorkbook) { public WriteContextImpl(WriteWorkbook writeWorkbook) {
if (writeWorkbook == null) { if (writeWorkbook == null) {
@ -249,23 +253,36 @@ public class WriteContextImpl implements WriteContext {
} }
@Override @Override
public void finish() { public void finish(boolean onException) {
if (finished) {
return;
}
finished = true;
WriteHandlerUtils.afterWorkbookDispose(this); WriteHandlerUtils.afterWorkbookDispose(this);
if (writeWorkbookHolder == null) { if (writeWorkbookHolder == null) {
return; return;
} }
Throwable throwable = null; Throwable throwable = null;
boolean isOutputStreamEncrypt = false; boolean isOutputStreamEncrypt = false;
try { // Determine if you need to write excel
isOutputStreamEncrypt = doOutputStreamEncrypt07(); boolean writeExcel = !onException;
} catch (Throwable t) { if (writeWorkbookHolder.getWriteExcelOnException()) {
throwable = t; writeExcel = Boolean.TRUE;
}
// No data is written if an exception is thrown
if (writeExcel) {
try {
isOutputStreamEncrypt = doOutputStreamEncrypt07();
} catch (Throwable t) {
throwable = t;
}
} }
if (!isOutputStreamEncrypt) { if (!isOutputStreamEncrypt) {
try { try {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); if (writeExcel) {
writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream());
}
writeWorkbookHolder.getWorkbook().close(); writeWorkbookHolder.getWorkbook().close();
} catch (Throwable t) { } catch (Throwable t) {
throwable = t; throwable = t;
@ -289,7 +306,7 @@ public class WriteContextImpl implements WriteContext {
throwable = t; throwable = t;
} }
if (!isOutputStreamEncrypt) { if (writeExcel && !isOutputStreamEncrypt) {
try { try {
doFileEncrypt07(); doFileEncrypt07();
} catch (Throwable t) { } catch (Throwable t) {

2
src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java

@ -37,6 +37,7 @@ import com.alibaba.excel.converters.string.StringBooleanConverter;
import com.alibaba.excel.converters.string.StringErrorConverter; import com.alibaba.excel.converters.string.StringErrorConverter;
import com.alibaba.excel.converters.string.StringNumberConverter; import com.alibaba.excel.converters.string.StringNumberConverter;
import com.alibaba.excel.converters.string.StringStringConverter; import com.alibaba.excel.converters.string.StringStringConverter;
import com.alibaba.excel.converters.url.UrlImageConverter;
/** /**
* Load default handler * Load default handler
@ -71,6 +72,7 @@ public class DefaultConverterLoader {
putWriteConverter(new InputStreamImageConverter()); putWriteConverter(new InputStreamImageConverter());
putWriteConverter(new ByteArrayImageConverter()); putWriteConverter(new ByteArrayImageConverter());
putWriteConverter(new BoxingByteArrayImageConverter()); putWriteConverter(new BoxingByteArrayImageConverter());
putWriteConverter(new UrlImageConverter());
return defaultWriteConverter; return defaultWriteConverter;
} }

52
src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java

@ -0,0 +1,52 @@
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();
}
}
}
}

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

@ -16,7 +16,7 @@ public abstract class AnalysisEventListener<T> implements ReadListener<T> {
@Override @Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) { public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context); invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
} }
/** /**

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

@ -1,5 +1,6 @@
package com.alibaba.excel.exception; package com.alibaba.excel.exception;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder; import com.alibaba.excel.write.builder.ExcelWriterBuilder;
@ -17,6 +18,10 @@ public class ExcelDataConvertException extends RuntimeException {
* NotNull. * NotNull.
*/ */
private Integer columnIndex; private Integer columnIndex;
/**
* NotNull.
*/
private CellData cellData;
/** /**
* Nullable.Only when the header is configured and when the class header is used is not null. * 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; private ExcelContentProperty excelContentProperty;
public ExcelDataConvertException(String message) { public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
super(message); ExcelContentProperty excelContentProperty, String message) {
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message) {
super(message); super(message);
this.rowIndex = rowIndex; this.rowIndex = rowIndex;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty, public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
String message, Throwable cause) { ExcelContentProperty excelContentProperty, String message, Throwable cause) {
super(message, cause); super(message, cause);
this.rowIndex = rowIndex; this.rowIndex = rowIndex;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public ExcelDataConvertException(String message, Throwable cause) {
super(message, cause);
}
public ExcelDataConvertException(Throwable cause) {
super(cause);
}
public Integer getRowIndex() { public Integer getRowIndex() {
return rowIndex; return rowIndex;
} }
@ -75,4 +70,12 @@ public class ExcelDataConvertException extends RuntimeException {
public void setExcelContentProperty(ExcelContentProperty excelContentProperty) { public void setExcelContentProperty(ExcelContentProperty excelContentProperty) {
this.excelContentProperty = excelContentProperty; this.excelContentProperty = excelContentProperty;
} }
public CellData getCellData() {
return cellData;
}
public void setCellData(CellData cellData) {
this.cellData = cellData;
}
} }

7
src/main/java/com/alibaba/excel/metadata/CellData.java

@ -229,18 +229,21 @@ public class CellData<T> {
@Override @Override
public String toString() { public String toString() {
if (type == null) { if (type == null) {
return "empty"; return StringUtils.EMPTY;
} }
switch (type) { switch (type) {
case NUMBER: case NUMBER:
return numberValue.toString(); return numberValue.toString();
case BOOLEAN: case BOOLEAN:
return booleanValue.toString(); return booleanValue.toString();
case DIRECT_STRING:
case STRING: case STRING:
case ERROR: case ERROR:
return stringValue; return stringValue;
case IMAGE:
return "image[" + imageValue.length + "]";
default: default:
return "empty"; return StringUtils.EMPTY;
} }
} }

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

@ -24,6 +24,7 @@ import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.exception.ExcelGenerateException; import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder; import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.StringUtils; import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder; import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
@ -119,51 +120,10 @@ public class ExcelHeadProperty {
if (headClazz == null) { if (headClazz == null) {
return; return;
} }
List<Field> fieldList = new ArrayList<Field>(); // Declared fields
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
List<Field> defaultFieldList = new ArrayList<Field>(); List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>(); Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
for (Field field : fieldList) { ClassUtils.declaredFields(headClazz, defaultFieldList, customFiledMap, ignoreMap, convertAllFiled);
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);
}
int index = 0; int index = 0;
for (Field field : defaultFieldList) { for (Field field : defaultFieldList) {

4
src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java

@ -166,7 +166,7 @@ public class ExcelReaderSheetBuilder {
* *
* @return * @return
*/ */
public List<Object> doReadSync() { public <T> List<T> doReadSync() {
if (excelReader == null) { if (excelReader == null) {
throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method"); throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method");
} }
@ -174,7 +174,7 @@ public class ExcelReaderSheetBuilder {
registerReadListener(syncReadListener); registerReadListener(syncReadListener);
excelReader.read(build()); excelReader.read(build());
excelReader.finish(); excelReader.finish();
return syncReadListener.getList(); return (List<T>)syncReadListener.getList();
} }
} }

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

@ -2,6 +2,7 @@ package com.alibaba.excel.read.listener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,7 +45,7 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
AnalysisContext context) { AnalysisContext context) {
int index = 0; int index = 0;
if (context.readWorkbookHolder().getDefaultReturnMap()) { if (context.readWorkbookHolder().getDefaultReturnMap()) {
Map<Integer, String> map = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1); Map<Integer, String> map = new LinkedHashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey(); Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
@ -92,7 +93,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
try { try {
resultModel = excelReadHeadProperty.getHeadClazz().newInstance(); resultModel = excelReadHeadProperty.getHeadClazz().newInstance();
} catch (Exception e) { } 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); "Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);
} }
Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap(); Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();

6
src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java

@ -190,8 +190,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) {
return; return;
} }
Map<Integer, String> dataMap = Map<Integer, String> dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext);
ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder());
ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty();
Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap(); Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap();
Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap();
@ -208,6 +207,9 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
List<String> headNameList = headData.getHeadNameList(); List<String> headNameList = headData.getHeadNameList();
String headName = headNameList.get(headNameList.size() - 1); String headName = headNameList.get(headNameList.size() - 1);
for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) { for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) {
if (stringEntry == null) {
continue;
}
String headString = stringEntry.getValue(); String headString = stringEntry.getValue();
Integer stringKey = stringEntry.getKey(); Integer stringKey = stringEntry.getKey();
if (StringUtils.isEmpty(headString)) { if (StringUtils.isEmpty(headString)) {

28
src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java

@ -26,10 +26,9 @@ public class ReadSheetHolder extends AbstractReadHolder {
*/ */
private String sheetName; private String sheetName;
/** /**
* get total row , Data may be inaccurate * Gets the total number of rows , data may be inaccurate
*/ */
@Deprecated private Integer approximateTotalRowNumber;
private Integer total;
public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) {
super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled()); super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled());
@ -71,12 +70,29 @@ public class ReadSheetHolder extends AbstractReadHolder {
this.sheetName = sheetName; this.sheetName = sheetName;
} }
/**
*
* Approximate total number of rows
*
* @return
* @see #getApproximateTotalRowNumber()
*/
@Deprecated
public Integer getTotal() { public Integer getTotal() {
return total; return approximateTotalRowNumber;
}
/**
* Approximate total number of rows
*
* @return
*/
public Integer getApproximateTotalRowNumber() {
return approximateTotalRowNumber;
} }
public void setTotal(Integer total) { public void setApproximateTotalRowNumber(Integer approximateTotalRowNumber) {
this.total = total; this.approximateTotalRowNumber = approximateTotalRowNumber;
} }
@Override @Override

150
src/main/java/com/alibaba/excel/util/ClassUtils.java

@ -0,0 +1,150 @@
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;
}
}
}

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

@ -6,6 +6,7 @@ import java.lang.reflect.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter; import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild; import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum; import com.alibaba.excel.enums.CellDataTypeEnum;
@ -28,28 +29,37 @@ public class ConverterUtils {
* Convert it into a String map * Convert it into a String map
* *
* @param cellDataMap * @param cellDataMap
* @param readHolder * @param context
* @return * @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); 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()) { for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
CellData cellData = entry.getValue(); CellData cellData = entry.getValue();
while (index < key) {
stringMap.put(index, null);
index++;
}
index++;
if (cellData.getType() == CellDataTypeEnum.EMPTY) { if (cellData.getType() == CellDataTypeEnum.EMPTY) {
stringMap.put(entry.getKey(), null); stringMap.put(key, null);
continue; continue;
} }
Converter converter = Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType())); currentReadHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException( throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Converter not found, convert " + cellData.getType() + " to String"); "Converter not found, convert " + cellData.getType() + " to String");
} }
try { try {
stringMap.put(entry.getKey(), stringMap.put(key,
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration()))); (String)(converter.convertToJavaData(cellData, null, currentReadHolder.globalConfiguration())));
} catch (Exception e) { } 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; return stringMap;
@ -116,13 +126,13 @@ public class ConverterUtils {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
} }
if (converter == null) { if (converter == null) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); "Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
} }
try { try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration); return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty, throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Convert data " + cellData + " to " + clazz + " error ", e); "Convert data " + cellData + " to " + clazz + " error ", e);
} }
} }

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

@ -12,6 +12,7 @@ import com.alibaba.excel.exception.ExcelDataConvertException;
* @author Jiaju Zhuang * @author Jiaju Zhuang
**/ **/
public class DateUtils { 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_14 = "yyyyMMddHHmmss";
public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss"; public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss";
public static final String DATE_FORMAT_19 = "yyyy-MM-dd 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; return DATE_FORMAT_17;
case 14: case 14:
return DATE_FORMAT_14; return DATE_FORMAT_14;
case 10:
return DATE_FORMAT_10;
default: default:
throw new ExcelDataConvertException("can not find date format for:" + dateString); throw new IllegalArgumentException("can not find date format for:" + dateString);
} }
} }

5
src/main/java/com/alibaba/excel/write/ExcelBuilder.java

@ -71,11 +71,14 @@ public interface ExcelBuilder {
/** /**
* Close io * Close io
*
* @param onException
*/ */
void finish(); void finish(boolean onException);
/** /**
* add password * add password
*
* @param data * @param data
* @param writeSheet * @param writeSheet
* @param writeTable * @param writeTable

24
src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java

@ -31,10 +31,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
FileUtils.createPoiFilesDirectory(); FileUtils.createPoiFilesDirectory();
context = new WriteContextImpl(writeWorkbook); context = new WriteContextImpl(writeWorkbook);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
@ -57,10 +57,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteAddExecutor.add(data); excelWriteAddExecutor.add(data);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
@ -80,18 +80,22 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteFillExecutor.fill(data, fillConfig); excelWriteFillExecutor.fill(data, fillConfig);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }
private void finishOnException() {
finish(true);
}
@Override @Override
public void finish() { public void finish(boolean onException) {
if (context != null) { if (context != null) {
context.finish(); context.finish(onException);
} }
} }
@ -108,10 +112,10 @@ public class ExcelBuilderImpl implements ExcelBuilder {
} }
excelWriteAddExecutor.add(data); excelWriteAddExecutor.add(data);
} catch (RuntimeException e) { } catch (RuntimeException e) {
finish(); finishOnException();
throw e; throw e;
} catch (Throwable e) { } catch (Throwable e) {
finish(); finishOnException();
throw new ExcelGenerateException(e); throw new ExcelGenerateException(e);
} }
} }

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

@ -146,6 +146,14 @@ public class ExcelWriterBuilder {
return this; 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 * The default is all excel objects.if true , you can use {@link com.alibaba.excel.annotation.ExcelIgnore} ignore a
* field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed. * field. if false , you must use {@link com.alibaba.excel.annotation.ExcelProperty} to use a filed.

16
src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java

@ -59,8 +59,9 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
case EMPTY: case EMPTY:
return cellData; return cellData;
default: default:
throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(), cellData,
+ "at row:" + cell.getRow().getRowNum()); 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)); converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz));
} }
if (converter == null) { 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() + "."); "Can not find 'Converter' support class " + clazz.getSimpleName() + ".");
} }
CellData cellData; CellData cellData;
@ -110,11 +112,13 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
cellData = cellData =
converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration()); converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration());
} catch (Exception e) { } catch (Exception e) {
throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
e); new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), e);
} }
if (cellData == null || cellData.getType() == null) { 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()); "Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum());
} }
return cellData; return cellData;

18
src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java

@ -2,7 +2,6 @@ package com.alibaba.excel.write.executor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; 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.context.WriteContext;
import com.alibaba.excel.enums.HeadKindEnum; import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head; import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty; import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.CollectionUtils; import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.WorkBookUtil; import com.alibaba.excel.util.WorkBookUtil;
import com.alibaba.excel.util.WriteHandlerUtils; import com.alibaba.excel.util.WriteHandlerUtils;
@ -158,13 +157,11 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
continue; continue;
} }
Object value = beanMap.get(filedName); Object value = beanMap.get(filedName);
if (value == null) {
continue;
}
WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE); WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE);
Cell cell = WorkBookUtil.createCell(row, cellIndex++); Cell cell = WorkBookUtil.createCell(row, cellIndex++);
WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE); 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); WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
} }
} }
@ -173,13 +170,8 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
if (!fieldList.isEmpty()) { if (!fieldList.isEmpty()) {
return; return;
} }
Class tempClass = clazz; ClassUtils.declaredFields(clazz, fieldList,
while (tempClass != null) { writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled());
if (tempClass != BaseRowModel.class) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
}
tempClass = tempClass.getSuperclass();
}
} }
} }

46
src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java

@ -3,9 +3,11 @@ package com.alibaba.excel.write.executor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellStyle;
@ -57,6 +59,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
*/ */
private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache = private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache =
new HashMap<Integer, Map<AnalysisCell, CellStyle>>(8); 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 * Last index cache for collection fields
*/ */
@ -121,7 +127,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (collectionLastIndexMap == null) { if (collectionLastIndexMap == null) {
number--; number--;
} }
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number); sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false);
for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) { for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) {
if (analysisCell.getRowIndex() > maxRowIndex) { if (analysisCell.getRowIndex() > maxRowIndex) {
analysisCell.setRowIndex(analysisCell.getRowIndex() + number); analysisCell.setRowIndex(analysisCell.getRowIndex() + number);
@ -245,7 +251,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} else { } else {
row = sheet.createRow(lastRowIndex); row = sheet.createRow(lastRowIndex);
} }
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE); WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE);
} else {
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
} }
} }
Cell cell = row.getCell(lastColumnIndex); Cell cell = row.getCell(lastColumnIndex);
@ -271,6 +280,21 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
return cell; 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) { private List<AnalysisCell> readTemplateData(Map<Integer, List<AnalysisCell>> analysisCache) {
Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); Integer sheetNo = writeContext.writeSheetHolder().getSheetNo();
List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo); List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo);
@ -280,6 +304,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); Sheet sheet = writeContext.writeSheetHolder().getCachedSheet();
analysisCellList = new ArrayList<AnalysisCell>(); analysisCellList = new ArrayList<AnalysisCell>();
List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>(); List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>();
Set<Integer> firstColumnCache = new HashSet<Integer>();
for (int i = 0; i <= sheet.getLastRowNum(); i++) { for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i); Row row = sheet.getRow(i);
if (row == null) { if (row == null) {
@ -290,7 +315,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (cell == null) { if (cell == null) {
continue; 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 // Prevent empty data from not being replaced
if (preparedData != null) { if (preparedData != null) {
cell.setCellValue(preparedData); cell.setCellValue(preparedData);
@ -310,10 +336,11 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
* @param collectionAnalysisCellList * @param collectionAnalysisCellList
* @param rowIndex * @param rowIndex
* @param columnIndex * @param columnIndex
* @param firstColumnCache
* @return Returns the data that the cell needs to replace * @return Returns the data that the cell needs to replace
*/ */
private String prepareData(Cell cell, List<AnalysisCell> analysisCellList, 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())) { if (!CellType.STRING.equals(cell.getCellTypeEnum())) {
return null; return null;
} }
@ -323,6 +350,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
StringBuilder preparedData = new StringBuilder(); StringBuilder preparedData = new StringBuilder();
AnalysisCell analysisCell = null; AnalysisCell analysisCell = null;
int startIndex = 0; int startIndex = 0;
int length = value.length(); int length = value.length();
int lastPrepareDataIndex = 0; int lastPrepareDataIndex = 0;
@ -376,6 +404,13 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} }
lastPrepareDataIndex = suffixIndex + 1; 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 (analysisCell != null) {
if (lastPrepareDataIndex == length) { if (lastPrepareDataIndex == length) {
analysisCell.getPrepareDataList().add(StringUtils.EMPTY); analysisCell.getPrepareDataList().add(StringUtils.EMPTY);
@ -386,6 +421,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) {
analysisCellList.add(analysisCell); analysisCellList.add(analysisCell);
} else { } else {
if (!firstColumnCache.contains(rowIndex)) {
analysisCell.setFirstColumn(Boolean.TRUE);
firstColumnCache.add(rowIndex);
}
collectionAnalysisCellList.add(analysisCell); collectionAnalysisCellList.add(analysisCell);
} }
return preparedData.toString(); return preparedData.toString();
@ -403,6 +442,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
List<String> prepareDataList = new ArrayList<String>(); List<String> prepareDataList = new ArrayList<String>();
analysisCell.setPrepareDataList(prepareDataList); analysisCell.setPrepareDataList(prepareDataList);
analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON);
analysisCell.setFirstColumn(Boolean.FALSE);
return analysisCell; return analysisCell;
} }

19
src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java

@ -13,24 +13,39 @@ import com.alibaba.excel.metadata.Head;
*/ */
public class LoopMergeStrategy extends AbstractMergeStrategy { public class LoopMergeStrategy extends AbstractMergeStrategy {
private int eachRow; private int eachRow;
private int columnCount;
private int columnIndex; private int columnIndex;
public LoopMergeStrategy(int eachRow, int columnIndex) { public LoopMergeStrategy(int eachRow, int columnIndex) {
this(eachRow, 1, columnIndex);
}
public LoopMergeStrategy(int eachRow, int columnCount, int columnIndex) {
if (eachRow < 1) { if (eachRow < 1) {
throw new IllegalArgumentException("EachRows must be greater than 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) { if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0"); throw new IllegalArgumentException("ColumnIndex must be greater than 0");
} }
this.eachRow = eachRow; this.eachRow = eachRow;
this.columnCount = columnCount;
this.columnIndex = columnIndex; this.columnIndex = columnIndex;
} }
@Override @Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) { protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) { if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(), CellRangeAddress cellRangeAddress = new CellRangeAddress(
cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex()); cell.getRowIndex(),
cell.getRowIndex() + eachRow - 1,
cell.getColumnIndex(),
cell.getColumnIndex() + columnCount - 1);
sheet.addMergedRegion(cellRangeAddress); sheet.addMergedRegion(cellRangeAddress);
} }
} }

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

@ -63,6 +63,10 @@ public class WriteWorkbook extends WriteBasicParameter {
* Comment and RichTextString are only supported in memory mode. * Comment and RichTextString are only supported in memory mode.
*/ */
private Boolean inMemory; 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. * The default is all excel objects.Default is true.
* <p> * <p>
@ -169,4 +173,12 @@ public class WriteWorkbook extends WriteBasicParameter {
public void setInMemory(Boolean inMemory) { public void setInMemory(Boolean inMemory) {
this.inMemory = inMemory; this.inMemory = inMemory;
} }
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
} }

9
src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java

@ -16,6 +16,7 @@ public class AnalysisCell {
private List<String> prepareDataList; private List<String> prepareDataList;
private Boolean onlyOneVariable; private Boolean onlyOneVariable;
private WriteTemplateAnalysisCellTypeEnum cellType; private WriteTemplateAnalysisCellTypeEnum cellType;
private Boolean firstColumn;
public int getColumnIndex() { public int getColumnIndex() {
return columnIndex; return columnIndex;
@ -65,6 +66,14 @@ public class AnalysisCell {
this.cellType = cellType; this.cellType = cellType;
} }
public Boolean getFirstColumn() {
return firstColumn;
}
public void setFirstColumn(Boolean firstColumn) {
this.firstColumn = firstColumn;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) { if (this == o) {

22
src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java

@ -103,6 +103,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
* Comment and RichTextString are only supported in memory mode. * Comment and RichTextString are only supported in memory mode.
*/ */
private Boolean inMemory; 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) { public WriteWorkbookHolder(WriteWorkbook writeWorkbook) {
super(writeWorkbook, null, writeWorkbook.getConvertAllFiled()); super(writeWorkbook, null, writeWorkbook.getConvertAllFiled());
@ -128,7 +132,10 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
throw new ExcelGenerateException("Copy template failure.", e); throw new ExcelGenerateException("Copy template failure.", e);
} }
if (writeWorkbook.getExcelType() == null) { 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; this.excelType = ExcelTypeEnum.XLS;
} else { } else {
this.excelType = ExcelTypeEnum.XLSX; this.excelType = ExcelTypeEnum.XLSX;
@ -148,6 +155,11 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
} else { } else {
this.inMemory = writeWorkbook.getInMemory(); this.inMemory = writeWorkbook.getInMemory();
} }
if (writeWorkbook.getWriteExcelOnException() == null) {
this.writeExcelOnException = Boolean.FALSE;
} else {
this.writeExcelOnException = writeWorkbook.getWriteExcelOnException();
}
} }
private void copyTemplate() throws IOException { private void copyTemplate() throws IOException {
@ -281,6 +293,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
this.inMemory = inMemory; this.inMemory = inMemory;
} }
public Boolean getWriteExcelOnException() {
return writeExcelOnException;
}
public void setWriteExcelOnException(Boolean writeExcelOnException) {
this.writeExcelOnException = writeExcelOnException;
}
@Override @Override
public HolderEnum holderType() { public HolderEnum holderType() {
return HolderEnum.WORKBOOK; return HolderEnum.WORKBOOK;

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

@ -19,12 +19,15 @@ import com.alibaba.excel.write.metadata.fill.FillConfig;
/** /**
* 写的填充写法 * 写的填充写法
* *
* @since 2.1.1
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
@Ignore @Ignore
public class FillTest { public class FillTest {
/** /**
* 最简单的填充 * 最简单的填充
*
* @since 2.1.1
*/ */
@Test @Test
public void simpleFill() { public void simpleFill() {
@ -51,6 +54,8 @@ public class FillTest {
/** /**
* 填充列表 * 填充列表
*
* @since 2.1.1
*/ */
@Test @Test
public void listFill() { public void listFill() {
@ -76,6 +81,8 @@ public class FillTest {
/** /**
* 复杂的填充 * 复杂的填充
*
* @since 2.1.1
*/ */
@Test @Test
public void complexFill() { public void complexFill() {
@ -105,6 +112,8 @@ public class FillTest {
* 数据量大的复杂填充 * 数据量大的复杂填充
* <p> * <p>
* 这里的解决方案是 确保模板list为最后一行然后再拼接table.还有03版没救只能刚正面加内存 * 这里的解决方案是 确保模板list为最后一行然后再拼接table.还有03版没救只能刚正面加内存
*
* @since 2.1.1
*/ */
@Test @Test
public void complexFillWithTable() { public void complexFillWithTable() {
@ -145,6 +154,8 @@ public class FillTest {
/** /**
* 横向的填充 * 横向的填充
*
* @since 2.1.1
*/ */
@Test @Test
public void horizontalFill() { public void horizontalFill() {

15
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDAO.java

@ -0,0 +1,15 @@
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,所有数据一次性插入
}
}

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

@ -23,19 +23,52 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<DemoData> list = new ArrayList<DemoData>(); 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 @Override
public void invoke(DemoData data, AnalysisContext context) { public void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) { if (list.size() >= BATCH_COUNT) {
saveData(); saveData();
// 存储完成清理 list
list.clear(); list.clear();
} }
} }
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); LOGGER.info("所有数据解析完成!");
} }
@ -45,6 +78,7 @@ public class DemoDataListener extends AnalysisEventListener<DemoData> {
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!"); LOGGER.info("存储数据库成功!");
} }
} }

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

@ -39,8 +39,8 @@ public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoDa
// 如果要获取头的信息 配合invokeHeadMap使用 // 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) { if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex()); excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
} }
} }

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

@ -38,8 +38,8 @@ public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) { if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(), LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex()); excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
} }
} }

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

@ -202,17 +202,15 @@ public class ReadTest {
public void synchronousRead() { public void synchronousRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
List<Object> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync(); List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (Object obj : list) { for (DemoData data : list) {
DemoData data = (DemoData)obj;
LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
} }
// 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
list = EasyExcel.read(fileName).sheet().doReadSync(); List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
for (Object obj : list) { for (Map<Integer, String> data : listMap) {
// 返回每条数据的键值对 表示所在的列 和所在列的值 // 返回每条数据的键值对 表示所在的列 和所在列的值
Map<Integer, String> data = (Map<Integer, String>)obj;
LOGGER.info("读取到数据:{}", JSON.toJSONString(data)); LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
} }
} }

20
src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDAO.java

@ -0,0 +1,20 @@
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,所有数据一次性插入
}
}

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

@ -15,26 +15,61 @@ import com.alibaba.fastjson.JSON;
* *
* @author Jiaju Zhuang * @author Jiaju Zhuang
*/ */
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class UploadDataListener extends AnalysisEventListener<UploadData> { 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 方便内存回收 * 每隔5条存储数据库实际使用中可以3000条然后清理list 方便内存回收
*/ */
private static final int BATCH_COUNT = 5; private static final int BATCH_COUNT = 5;
List<UploadData> list = new ArrayList<UploadData>(); 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 @Override
public void invoke(UploadData data, AnalysisContext context) { public void invoke(UploadData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data); list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) { if (list.size() >= BATCH_COUNT) {
saveData(); saveData();
// 存储完成清理 list
list.clear(); list.clear();
} }
} }
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData(); saveData();
LOGGER.info("所有数据解析完成!"); LOGGER.info("所有数据解析完成!");
} }
@ -44,6 +79,7 @@ public class UploadDataListener extends AnalysisEventListener<UploadData> {
*/ */
private void saveData() { private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size()); LOGGER.info("{}条数据,开始存储数据库!", list.size());
uploadDAO.save(list);
LOGGER.info("存储数据库成功!"); LOGGER.info("存储数据库成功!");
} }
} }

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

@ -2,14 +2,15 @@ package com.alibaba.easyexcel.test.demo.web;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; 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 org.springframework.web.multipart.MultipartFile;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
/** /**
* web读写案例 * web读写案例
@ -25,8 +27,12 @@ import com.alibaba.excel.EasyExcel;
**/ **/
@Controller @Controller
public class WebTest { public class WebTest {
@Autowired
private UploadDAO uploadDAO;
/** /**
* 文件下载 * 文件下载失败了会返回一个有部分数据的Excel
* <p> * <p>
* 1. 创建excel对应的实体对象 参照{@link DownloadData} * 1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p> * <p>
@ -45,6 +51,35 @@ public class WebTest {
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); 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> * <p>
@ -57,7 +92,7 @@ public class WebTest {
@PostMapping("upload") @PostMapping("upload")
@ResponseBody @ResponseBody
public String upload(MultipartFile file) throws IOException { 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"; return "success";
} }

7
src/test/java/com/alibaba/easyexcel/test/demo/write/ImageData.java

@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.demo.write;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth; import com.alibaba.excel.annotation.write.style.ColumnWidth;
@ -27,4 +28,10 @@ public class ImageData {
@ExcelProperty(converter = StringImageConverter.class) @ExcelProperty(converter = StringImageConverter.class)
private String string; private String string;
private byte[] byteArray; private byte[] byteArray;
/**
* 根据url导出
*
* @since 2.1.1
*/
private URL url;
} }

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

@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.demo.write;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -215,12 +216,14 @@ public class WriteTest {
ImageData imageData = new ImageData(); ImageData imageData = new ImageData();
list.add(imageData); list.add(imageData);
String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";
// 放入种类型的图片 实际使用只要选一种即可 // 放入种类型的图片 实际使用只要选一种即可
imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));
imageData.setFile(new File(imagePath)); imageData.setFile(new File(imagePath));
imageData.setString(imagePath); imageData.setString(imagePath);
inputStream = FileUtils.openInputStream(new File(imagePath)); inputStream = FileUtils.openInputStream(new File(imagePath));
imageData.setInputStream(inputStream); 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); EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);
} finally { } finally {
if (inputStream != null) { if (inputStream != null) {

110
src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java

@ -0,0 +1,110 @@
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;
}
}

4
src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java

@ -79,11 +79,11 @@ public class PoiTest {
@Test @Test
public void lastRowNum255() throws IOException, InvalidFormatException { 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)); XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file));
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook);
Sheet xssfSheet = xssfWorkbook.getSheetAt(0); 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"); FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx");
sxssfWorkbook.write(fileout); sxssfWorkbook.write(fileout);

43
src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java

@ -0,0 +1,43 @@
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("所有数据解析完成!");
}
}

20
src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java

@ -0,0 +1,20 @@
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;
}

28
src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java

@ -0,0 +1,28 @@
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();
}
}

15
update.md

@ -1,3 +1,18 @@
# 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 # 2.1.0-beta4
* 修改最长匹配策略会空指针的bug [Issue #747](https://github.com/alibaba/easyexcel/issues/747) * 修改最长匹配策略会空指针的bug [Issue #747](https://github.com/alibaba/easyexcel/issues/747)
* 修改afterRowDispose错误 [Issue #751](https://github.com/alibaba/easyexcel/issues/751) * 修改afterRowDispose错误 [Issue #751](https://github.com/alibaba/easyexcel/issues/751)

Loading…
Cancel
Save