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

Merge pull request #523 from alibaba/v2.0.0-beta2

V2.0.0 beta2
......@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.0.0-beta1</version>
<version>2.0.0-beta2</version>
<packaging>jar</packaging>
<name>easyexcel</name>
......
# easyexcel核心功能
## 目录
### 前言
#### 关于@Data
读写的对象都用到了[Lombok](https://www.projectlombok.org/),他会自动生成`get`,`set` ,如果不需要自己创建对象并生成`get`,`set`
#### 以下功能目前不支持
* 单个文件的并发写入、读取
* 读取图片
*
#### 开源项目不容易,如果觉得本项目对您的工作还是有帮助的话,请在右上角帮忙点个★Star。
### 读
DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java)
* [最简单的读](#simpleRead)
......@@ -853,6 +861,67 @@ public class LongestMatchColumnWidthData {
![img](img/readme/quickstart/write/customHandlerWrite.png)
##### 对象
参照:[对象](#simpleWriteObject)
##### 定义拦截器
````java
/**
* 自定义拦截器。对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel
*
* @author Jiaju Zhuang
*/
public class CustomCellWriteHandler implements CellWriteHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomCellWriteHandler.class);
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Head head, int relativeRowIndex, boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData,
Cell cell, Head head, int relativeRowIndex, boolean isHead) {
// 这里可以对cell进行任何操作
LOGGER.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex());
if (isHead && cell.getColumnIndex() == 0) {
CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper();
Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL);
hyperlink.setAddress("https://github.com/alibaba/easyexcel");
cell.setHyperlink(hyperlink);
}
}
}
````
````java
/**
* 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2
*
* @author Jiaju Zhuang
*/
public class CustomSheetWriteHandler implements SheetWriteHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomSheetWriteHandler.class);
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
LOGGER.info("第{}个Sheet写入成功。", writeSheetHolder.getSheetNo());
// 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0);
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"});
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
writeSheetHolder.getSheet().addValidationData(dataValidation);
}
}
````
##### 代码
```java
/**
......
......@@ -116,6 +116,20 @@ public class ExcelAnalyserImpl implements ExcelAnalyser {
} catch (Throwable e) {
throw new ExcelAnalysisException("Can not close IO", e);
}
try {
if (readWorkbookHolder.getOpcPackage() != null) {
readWorkbookHolder.getOpcPackage().close();
}
} catch (Throwable e) {
throw new ExcelAnalysisException("Can not close IO", e);
}
try {
if (readWorkbookHolder.getPoifsFileSystem() != null) {
readWorkbookHolder.getPoifsFileSystem().close();
}
} catch (Throwable e) {
throw new ExcelAnalysisException("Can not close IO", e);
}
try {
if (analysisContext.readWorkbookHolder().getAutoCloseStream()
&& readWorkbookHolder.getInputStream() != null) {
......
......@@ -73,6 +73,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor {
this.analysisContext = context;
this.records = new TreeMap<Integer, CellData>();
this.poifsFileSystem = poifsFileSystem;
analysisContext.readWorkbookHolder().setPoifsFileSystem(poifsFileSystem);
}
@Override
......@@ -130,6 +131,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelExecutor {
thisColumn = handler.getColumn();
cellData = handler.getCellData();
if (cellData != null) {
cellData.checkEmpty();
records.put(thisColumn, cellData);
}
break;
......
......@@ -16,11 +16,11 @@ public class SharedStringsTableHandler extends DefaultHandler {
/**
* The final piece of data
*/
private String currentData;
private StringBuilder currentData;
/**
* Current element data
*/
private String currentElementData;
private StringBuilder currentElementData;
private ReadCache readCache;
......@@ -30,22 +30,36 @@ public class SharedStringsTableHandler extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) {
if (SI_TAG.equals(name)) {
currentData = "";
if (T_TAG.equals(name)) {
currentElementData = null;
} else if (SI_TAG.equals(name)) {
currentData = null;
}
}
@Override
public void endElement(String uri, String localName, String name) {
if (T_TAG.equals(name)) {
currentData += currentElementData;
if (currentElementData != null) {
if (currentData == null) {
currentData = new StringBuilder();
}
currentData.append(currentElementData);
}
} else if (SI_TAG.equals(name)) {
readCache.put(currentData);
if (currentData == null) {
readCache.put(null);
} else {
readCache.put(currentData.toString());
}
}
}
@Override
public void characters(char[] ch, int start, int length) {
currentElementData = new String(ch, start, length);
if (currentElementData == null) {
currentElementData = new StringBuilder();
}
currentElementData.append(new String(ch, start, length));
}
}
......@@ -59,6 +59,7 @@ public class XlsxSaxAnalyser implements ExcelExecutor {
ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder();
OPCPackage pkg = readOpcPackage(readWorkbookHolder, decryptedStream);
readWorkbookHolder.setOpcPackage(pkg);
PackagePart sharedStringsTablePackagePart =
pkg.getPartsByContentType(XSSFRelation.SHARED_STRINGS.getContentType()).get(0);
......
......@@ -120,6 +120,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
} else if (currentCellData.getType() == CellDataTypeEnum.DIRECT_STRING) {
currentCellData.setType(CellDataTypeEnum.STRING);
}
currentCellData.checkEmpty();
curRowContent.put(curCol, currentCellData);
}
// This is a special form of string
......@@ -130,6 +131,7 @@ public class DefaultCellHandler implements XlsxCellHandler, XlsxRowResultHolder
stringValue = stringValue.trim();
}
currentCellData.setStringValue(stringValue);
currentCellData.checkEmpty();
curRowContent.put(curCol, currentCellData);
}
}
......
package com.alibaba.excel.metadata;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.excel.converters.Converter;
......@@ -22,7 +21,7 @@ public class BasicParameter {
/**
* Custom type conversions override the default
*/
private List<Converter> customConverterList = new ArrayList<Converter>();
private List<Converter> customConverterList;
/**
* Automatic trim includes sheet name and content
*/
......
package com.alibaba.excel.metadata;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.util.StringUtils;
/**
* Excel internal cell data
......@@ -168,6 +169,31 @@ public class CellData {
this.dataFormatString = dataFormatString;
}
/**
* Ensure that the object does not appear null
*/
public void checkEmpty() {
switch (type) {
case STRING:
case ERROR:
if (StringUtils.isEmpty(stringValue)) {
type = CellDataTypeEnum.EMPTY;
}
return;
case NUMBER:
if (doubleValue == null) {
type = CellDataTypeEnum.EMPTY;
}
return;
case BOOLEAN:
if (booleanValue == null) {
type = CellDataTypeEnum.EMPTY;
}
return;
default:
}
}
@Override
public String toString() {
switch (type) {
......
......@@ -137,8 +137,8 @@ public class ExcelHeadProperty {
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelGenerateException("The index of " + customFiledMap.get(excelProperty.index()).getName()
+ " and " + field.getName() + " must be inconsistent");
throw new ExcelGenerateException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
}
......
......@@ -6,6 +6,8 @@ import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -90,6 +92,14 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
* Prevent repeating sheet
*/
private Set<Integer> hasReadSheet;
/**
* Package
*/
private OPCPackage opcPackage;
/**
* File System
*/
private POIFSFileSystem poifsFileSystem;
public ReadWorkbookHolder(ReadWorkbook readWorkbook) {
super(readWorkbook, null, readWorkbook.getConvertAllFiled());
......@@ -232,6 +242,22 @@ public class ReadWorkbookHolder extends AbstractReadHolder {
this.defaultReturnMap = defaultReturnMap;
}
public OPCPackage getOpcPackage() {
return opcPackage;
}
public void setOpcPackage(OPCPackage opcPackage) {
this.opcPackage = opcPackage;
}
public POIFSFileSystem getPoifsFileSystem() {
return poifsFileSystem;
}
public void setPoifsFileSystem(POIFSFileSystem poifsFileSystem) {
this.poifsFileSystem = poifsFileSystem;
}
@Override
public HolderEnum holderType() {
return HolderEnum.WORKBOOK;
......
......@@ -10,6 +10,8 @@ public class BooleanUtils {
private static final String TRUE_NUMBER = "1";
private static final String FALSE_NUMBER = "0";
private BooleanUtils() {}
/**
* String to boolean
*
......
......@@ -5,11 +5,13 @@ import java.util.Map;
/**
* Collection utils
*
*
* @author jipengfei
*/
public class CollectionUtils {
private CollectionUtils() {}
public static boolean isEmpty(Collection<?> collection) {
return (collection == null || collection.isEmpty());
}
......
......@@ -25,6 +25,8 @@ public class FileUtils {
private static final String CACHE = "excache";
private static final int WRITE_BUFF_SIZE = 8192;
private FileUtils() {}
/**
* Reads the contents of a file into a byte array. * The file is always closed.
*
......
......@@ -17,6 +17,8 @@ public class IoUtils {
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private IoUtils() {}
/**
* Gets the contents of an InputStream as a byte[].
*
......
......@@ -5,6 +5,8 @@ package com.alibaba.excel.util;
*/
public class PositionUtils {
private PositionUtils() {}
public static int getRow(String currentCellIndex) {
int row = 0;
if (currentCellIndex != null) {
......
......@@ -2,11 +2,13 @@ package com.alibaba.excel.util;
/**
* String utils
*
*
* @author jipengfei
*/
public class StringUtils {
private StringUtils() {}
public static boolean isEmpty(Object str) {
return (str == null || "".equals(str));
}
......
......@@ -17,6 +17,8 @@ import com.alibaba.excel.write.metadata.style.WriteFont;
*/
public class StyleUtil {
private StyleUtil() {}
/**
* @param workbook
* @return
......
......@@ -22,6 +22,8 @@ import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
*/
public class WorkBookUtil {
private WorkBookUtil() {}
public static Workbook createWorkBook(WriteWorkbookHolder writeWorkbookHolder)
throws IOException, InvalidFormatException {
if (ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
......
package com.alibaba.easyexcel.test.temp;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.easyexcel.test.demo.read.DemoData;
import com.alibaba.easyexcel.test.demo.read.DemoDataListener;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
/**
* 临时测试
*
* @author Jiaju Zhuang
**/
@Ignore
public class Lock2Test {
private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class);
@Test
public void test() throws Exception {
File file = new File("D:\\test\\test001.xlsx");
List<Object> list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync();
LOGGER.info("数据:{}", list.size());
for (Object data : list) {
LOGGER.info("返回数据:{}", JSON.toJSONString(data));
}
}
@Test
public void simpleRead() {
// 写法1:
String fileName = "D:\\test\\珠海 (1).xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, LockData.class, new LockDataListener()).sheet().doRead();
}
@Test
public void test2() throws Exception {
File file = new File("D:\\test\\converter03.xls");
List<Object> list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync();
LOGGER.info("数据:{}", list.size());
for (Object data : list) {
LOGGER.info("返回数据:{}", JSON.toJSONString(data));
}
LOGGER.info("文件状态:{}", file.exists());
file.delete();
Thread.sleep(500 * 1000);
}
}
package com.alibaba.easyexcel.test.temp;
import lombok.Data;
/**
* 基础数据类.这里的排序和excel里面的排序一致
*
* @author Jiaju Zhuang
**/
@Data
public class LockData {
private String string0;
private String string1;
private String string2;
private String string3;
private String string4;
private String string5;
private String string6;
private String string7;
private String string8;
}
package com.alibaba.easyexcel.test.temp;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.easyexcel.test.demo.read.DemoDataListener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
/**
* 模板的读取类
*
* @author Jiaju Zhuang
*/
public class LockDataListener extends AnalysisEventListener<LockData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
List<LockData> list = new ArrayList<LockData>();
@Override
public void invoke(LockData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
if (list.size() >= BATCH_COUNT) {
saveData();
list.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
LOGGER.info("存储数据库成功!");
}
}
package com.alibaba.easyexcel.test.temp.poi;
import java.io.File;
import java.io.IOException;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.easyexcel.test.util.TestFileUtil;
/**
* 测试poi
*
* @author Jiaju Zhuang
**/
@Ignore
public class Poi2Test {
private static final Logger LOGGER = LoggerFactory.getLogger(Poi2Test.class);
@Test
public void test() throws IOException {
String file = "D:\\test\\珠海.xlsx";
SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file));
SXSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum());
SXSSFRow row = xssfSheet.getRow(0);
LOGGER.info("第一行数据:{}", row);
}
@Test
public void lastRowNumXSSF() throws IOException {
String file = "D:\\test\\珠海.xlsx";
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file);
LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets());
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum());
XSSFRow row = xssfSheet.getRow(0);
LOGGER.info("第一行数据:{}", row);
xssfSheet.createRow(20);
LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum());
}
}
package com.alibaba.easyexcel.test.temp.simple;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
/**
* 测试poi
*
* @author Jiaju Zhuang
**/
@Ignore
public class HgTest {
private static final Logger LOGGER = LoggerFactory.getLogger(HgTest.class);
@Test
public void hh() throws IOException {
List<Object> list = EasyExcel.read(new FileInputStream("D:\\test\\hg2.xlsx")).autoTrim(Boolean.FALSE).sheet()
.headRowNumber(0).doReadSync();
for (Object data : list) {
LOGGER.info("返回数据:{}", JSON.toJSONString(data));
}
}
}
# 2.0.0
# 2.0.0-beta2
* 加速gc回收 [Issue #511](https://github.com/alibaba/easyexcel/issues/511)
* 修改空字符串读取可能读取上个字段的数据的bug
* 修改换行数据无法读取的bug [Issue #521](https://github.com/alibaba/easyexcel/issues/521)
* 修复在空字符串的时候 格式转换异常 [Issue #520](https://github.com/alibaba/easyexcel/issues/520)
# 2.0.0-beta1
* 优化读写逻辑
* 优化读写对外接口
* 加入转换器,方便格式转换
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册