提交 22dc33d4 编写于 作者: Z zhuangjiaju

优化读写逻辑

上级 99f041e9
......@@ -15,8 +15,8 @@
# JAVA解析Excel工具easyexcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
## 相关文档
* [关于软件](/abouteasyexcel.md)
* [快速使用](/quickstart.md)
* [关于软件](/abouteasyexcel.md)
* [常见问题](/problem.md)
* [更新记事](/update.md)
* [English-README](/easyexcel_en.md)
......@@ -74,7 +74,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja
* 文件下载
* <li>1. 创建excel对应的实体对象 参照{@link DownloadData}
* <li>2. 设置返回的 参数
* <li>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭异常问题不大
* <li>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
......
此差异已折叠。
......@@ -13,7 +13,6 @@ import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.enums.WriteLastRowType;
import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.WorkBookUtil;
......@@ -198,18 +197,13 @@ public class WriteContextImpl implements WriteContext {
if (!currentWriteHolder.needHead() || !currentWriteHolder.excelWriteHeadProperty().hasHead()) {
return;
}
int lastRowNum = writeSheetHolder.getSheet().getLastRowNum();
// 'lastRowNum' doesn't matter if it has one or zero,is's zero
if (WriteLastRowType.HAVE_DATA == writeSheetHolder.getWriteLastRowType()) {
lastRowNum++;
}
writeSheetHolder.setWriteLastRowType(WriteLastRowType.HAVE_DATA);
int rowIndex = lastRowNum + currentWriteHolder.relativeHeadRowIndex();
int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
newRowIndex += currentWriteHolder.relativeHeadRowIndex();
// Combined head
addMergedRegionToCurrentSheet(excelWriteHeadProperty, rowIndex);
for (int relativeRowIndex = 0, i = rowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + rowIndex;
addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex);
for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + newRowIndex;
i++, relativeRowIndex++) {
beforeRowCreate(rowIndex, relativeRowIndex);
beforeRowCreate(newRowIndex, relativeRowIndex);
Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i);
afterRowCreate(row, relativeRowIndex);
addOneRowOfHeadDataToExcel(row, excelWriteHeadProperty.getHeadMap(), relativeRowIndex);
......
......@@ -40,28 +40,34 @@ import com.alibaba.excel.converters.string.StringStringConverter;
* @author Jiaju Zhuang
*/
public class DefaultConverterLoader {
private static Map<String, Converter> defaultWriteConverter;
private static Map<String, Converter> allConverter;
/**
* Load default write converter
*
* @return
*/
public static Map<String, Converter> loadDefaultWriteConverter() {
Map<String, Converter> converterMap = new HashMap<String, Converter>(16);
putWriteConverter(converterMap, new BigDecimalNumberConverter());
putWriteConverter(converterMap, new BooleanBooleanConverter());
putWriteConverter(converterMap, new ByteNumberConverter());
putWriteConverter(converterMap, new DateStringConverter());
putWriteConverter(converterMap, new DoubleNumberConverter());
putWriteConverter(converterMap, new FloatNumberConverter());
putWriteConverter(converterMap, new IntegerNumberConverter());
putWriteConverter(converterMap, new LongNumberConverter());
putWriteConverter(converterMap, new ShortNumberConverter());
putWriteConverter(converterMap, new StringStringConverter());
return converterMap;
if (defaultWriteConverter != null) {
return defaultWriteConverter;
}
defaultWriteConverter = new HashMap<String, Converter>(16);
putWriteConverter(new BigDecimalNumberConverter());
putWriteConverter(new BooleanBooleanConverter());
putWriteConverter(new ByteNumberConverter());
putWriteConverter(new DateStringConverter());
putWriteConverter(new DoubleNumberConverter());
putWriteConverter(new FloatNumberConverter());
putWriteConverter(new IntegerNumberConverter());
putWriteConverter(new LongNumberConverter());
putWriteConverter(new ShortNumberConverter());
putWriteConverter(new StringStringConverter());
return defaultWriteConverter;
}
private static void putWriteConverter(Map<String, Converter> converterMap, Converter converter) {
converterMap.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
private static void putWriteConverter(Converter converter) {
defaultWriteConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
}
/**
......@@ -70,51 +76,63 @@ public class DefaultConverterLoader {
* @return
*/
public static Map<String, Converter> loadDefaultReadConverter() {
Map<String, Converter> converterMap = new HashMap<String, Converter>(64);
putReadConverter(converterMap, new BigDecimalBooleanConverter());
putReadConverter(converterMap, new BigDecimalNumberConverter());
putReadConverter(converterMap, new BigDecimalStringConverter());
putReadConverter(converterMap, new BooleanBooleanConverter());
putReadConverter(converterMap, new BooleanNumberConverter());
putReadConverter(converterMap, new BooleanStringConverter());
putReadConverter(converterMap, new ByteBooleanConverter());
putReadConverter(converterMap, new ByteNumberConverter());
putReadConverter(converterMap, new ByteStringConverter());
putReadConverter(converterMap, new DateNumberConverter());
putReadConverter(converterMap, new DateStringConverter());
putReadConverter(converterMap, new DoubleBooleanConverter());
putReadConverter(converterMap, new DoubleNumberConverter());
putReadConverter(converterMap, new DoubleStringConverter());
putReadConverter(converterMap, new FloatBooleanConverter());
putReadConverter(converterMap, new FloatNumberConverter());
putReadConverter(converterMap, new FloatStringConverter());
putReadConverter(converterMap, new IntegerBooleanConverter());
putReadConverter(converterMap, new IntegerNumberConverter());
putReadConverter(converterMap, new IntegerStringConverter());
putReadConverter(converterMap, new LongBooleanConverter());
putReadConverter(converterMap, new LongNumberConverter());
putReadConverter(converterMap, new LongStringConverter());
putReadConverter(converterMap, new ShortBooleanConverter());
putReadConverter(converterMap, new ShortNumberConverter());
putReadConverter(converterMap, new ShortStringConverter());
putReadConverter(converterMap, new StringBooleanConverter());
putReadConverter(converterMap, new StringNumberConverter());
putReadConverter(converterMap, new StringStringConverter());
putReadConverter(converterMap, new StringErrorConverter());
return converterMap;
return loadAllConverter();
}
/**
* Load all converter
*
* @return
*/
public static Map<String, Converter> loadAllConverter() {
if (allConverter != null) {
return allConverter;
}
allConverter = new HashMap<String, Converter>(64);
putAllConverter(new BigDecimalBooleanConverter());
putAllConverter(new BigDecimalNumberConverter());
putAllConverter(new BigDecimalStringConverter());
putAllConverter(new BooleanBooleanConverter());
putAllConverter(new BooleanNumberConverter());
putAllConverter(new BooleanStringConverter());
putAllConverter(new ByteBooleanConverter());
putAllConverter(new ByteNumberConverter());
putAllConverter(new ByteStringConverter());
putAllConverter(new DateNumberConverter());
putAllConverter(new DateStringConverter());
putAllConverter(new DoubleBooleanConverter());
putAllConverter(new DoubleNumberConverter());
putAllConverter(new DoubleStringConverter());
putAllConverter(new FloatBooleanConverter());
putAllConverter(new FloatNumberConverter());
putAllConverter(new FloatStringConverter());
putAllConverter(new IntegerBooleanConverter());
putAllConverter(new IntegerNumberConverter());
putAllConverter(new IntegerStringConverter());
putAllConverter(new LongBooleanConverter());
putAllConverter(new LongNumberConverter());
putAllConverter(new LongStringConverter());
putAllConverter(new ShortBooleanConverter());
putAllConverter(new ShortNumberConverter());
putAllConverter(new ShortStringConverter());
putAllConverter(new StringBooleanConverter());
putAllConverter(new StringNumberConverter());
putAllConverter(new StringStringConverter());
putAllConverter(new StringErrorConverter());
return allConverter;
}
private static void putReadConverter(Map<String, Converter> converterMap, Converter converter) {
converterMap.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
private static void putAllConverter(Converter converter) {
allConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
converter);
}
}
......@@ -7,11 +7,15 @@ package com.alibaba.excel.enums;
**/
public enum WriteLastRowType {
/**
* Tables are created without templates ,And any data has been written;
* Excel are created without templates ,And any data has been written;
*/
EMPTY,
COMMON_EMPTY,
/**
* It's supposed to have data in it.Tables are created with templates ,or any data has been written;
* Excel are created with templates ,And any data has been written;
*/
HAVE_DATA,;
TEMPLATE_EMPTY,
/**
* Any data has been written;
*/
HAS_DATA,;
}
......@@ -25,11 +25,20 @@ public class WorkBookUtil {
public static Workbook createWorkBook(WriteWorkbookHolder writeWorkbookHolder)
throws IOException, InvalidFormatException {
if (ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) {
XSSFWorkbook xssfWorkbook = null;
if (writeWorkbookHolder.getTemplateFile() != null) {
return new SXSSFWorkbook(new XSSFWorkbook(writeWorkbookHolder.getTemplateFile()));
xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateFile());
}
if (writeWorkbookHolder.getTemplateInputStream() != null) {
return new SXSSFWorkbook(new XSSFWorkbook(writeWorkbookHolder.getTemplateInputStream()));
xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTemplateInputStream());
}
// When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we
// are using the template, so we cache it
if (xssfWorkbook != null) {
for (int i = 0; i < xssfWorkbook.getNumberOfSheets(); i++) {
writeWorkbookHolder.getTemplateLastRowMap().put(i, xssfWorkbook.getSheetAt(i).getLastRowNum());
}
return new SXSSFWorkbook(xssfWorkbook);
}
return new SXSSFWorkbook(500);
}
......
......@@ -10,14 +10,12 @@ import java.util.Set;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.context.WriteContextImpl;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.WriteLastRowType;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.BaseRowModel;
......@@ -64,22 +62,14 @@ public class ExcelBuilderImpl implements ExcelBuilder {
return;
}
WriteSheetHolder writeSheetHolder = context.writeSheetHolder();
Sheet currentSheet = writeSheetHolder.getSheet();
int lastRowNum = currentSheet.getLastRowNum();
// 'lastRowNum' doesn't matter if it has one or zero,is's zero
if (lastRowNum == 0 && WriteLastRowType.EMPTY == writeSheetHolder.getWriteLastRowType()) {
lastRowNum--;
}
if (!data.isEmpty()) {
writeSheetHolder.setWriteLastRowType(WriteLastRowType.HAVE_DATA);
}
int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite();
if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) {
lastRowNum += context.currentWriteHolder().relativeHeadRowIndex();
newRowIndex += context.currentWriteHolder().relativeHeadRowIndex();
}
// Beanmap is out of order,so use fieldList
// BeanMap is out of order,so use fieldList
List<Field> fieldList = new ArrayList<Field>();
for (int relativeRowIndex = 0; relativeRowIndex < data.size(); relativeRowIndex++) {
int n = relativeRowIndex + lastRowNum + 1;
int n = relativeRowIndex + newRowIndex;
addOneRowOfDataToExcel(data.get(relativeRowIndex), n, relativeRowIndex, fieldList);
}
}
......
......@@ -13,21 +13,24 @@ import com.alibaba.excel.metadata.Head;
*/
public class LoopMergeStrategy extends AbstractMergeStrategy {
private int eachRow;
private int eachColumn;
private int columnIndex;
public LoopMergeStrategy(int eachRow, int eachColumn) {
if (eachRow < 1 || eachColumn < 1) {
throw new IllegalArgumentException("All parameters must be greater than 1");
public LoopMergeStrategy(int eachRow, int columnIndex) {
if (eachRow < 1) {
throw new IllegalArgumentException("EachRows must be greater than 1");
}
if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0");
}
this.eachRow = eachRow;
this.eachColumn = eachColumn;
this.columnIndex = columnIndex;
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
if (relativeRowIndex % eachRow == 0 && head.getColumnIndex() % eachColumn == 0) {
if (head.getColumnIndex() == columnIndex && relativeRowIndex % eachRow == 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(cell.getRowIndex(),
cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex() + eachColumn - 1);
cell.getRowIndex() + eachRow - 1, cell.getColumnIndex(), cell.getColumnIndex());
sheet.addMergedRegion(cellRangeAddress);
}
}
......
......@@ -7,6 +7,7 @@ import org.apache.poi.ss.usermodel.Sheet;
import com.alibaba.excel.enums.HolderEnum;
import com.alibaba.excel.enums.WriteLastRowType;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
/**
......@@ -60,9 +61,9 @@ public class WriteSheetHolder extends AbstractWriteHolder {
this.parentWriteWorkbookHolder = writeWorkbookHolder;
this.hasBeenInitializedTable = new HashMap<Integer, WriteTableHolder>();
if (writeWorkbookHolder.getTemplateInputStream() == null && writeWorkbookHolder.getTemplateFile() == null) {
writeLastRowType = WriteLastRowType.EMPTY;
writeLastRowType = WriteLastRowType.COMMON_EMPTY;
} else {
writeLastRowType = WriteLastRowType.HAVE_DATA;
writeLastRowType = WriteLastRowType.TEMPLATE_EMPTY;
}
}
......@@ -122,6 +123,36 @@ public class WriteSheetHolder extends AbstractWriteHolder {
this.writeLastRowType = writeLastRowType;
}
/**
* Get the last line of index,you have to make sure that the data is written next
*
* @return
*/
public int getNewRowIndexAndStartDoWrite() {
// 'getLastRowNum' doesn't matter if it has one or zero,is's zero
int newRowIndex = 0;
switch (writeLastRowType) {
case TEMPLATE_EMPTY:
if (parentWriteWorkbookHolder.getExcelType() == ExcelTypeEnum.XLSX) {
if (parentWriteWorkbookHolder.getTemplateLastRowMap().containsKey(sheetNo)) {
newRowIndex = parentWriteWorkbookHolder.getTemplateLastRowMap().get(sheetNo);
}
} else {
newRowIndex = sheet.getLastRowNum();
}
newRowIndex++;
break;
case HAS_DATA:
newRowIndex = sheet.getLastRowNum();
newRowIndex++;
break;
default:
break;
}
writeLastRowType = WriteLastRowType.HAS_DATA;
return newRowIndex;
}
@Override
public HolderEnum holderType() {
return HolderEnum.SHEET;
......
......@@ -72,6 +72,11 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
* prevent duplicate creation of sheet objects
*/
private Map<Integer, WriteSheetHolder> hasBeenInitializedSheet;
/**
* When using SXSSFWorkbook, you can't get the actual last line.But we need to read the last line when we are using
* the template, so we cache it
*/
private Map<Integer, Integer> templateLastRowMap;
public WriteWorkbookHolder(WriteWorkbook writeWorkbook) {
super(writeWorkbook, null, writeWorkbook.getConvertAllFiled());
......@@ -114,6 +119,7 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream();
}
this.hasBeenInitializedSheet = new HashMap<Integer, WriteSheetHolder>();
this.templateLastRowMap = new HashMap<Integer, Integer>(8);
}
public Workbook getWorkbook() {
......@@ -196,6 +202,14 @@ public class WriteWorkbookHolder extends AbstractWriteHolder {
this.mandatoryUseInputStream = mandatoryUseInputStream;
}
public Map<Integer, Integer> getTemplateLastRowMap() {
return templateLastRowMap;
}
public void setTemplateLastRowMap(Map<Integer, Integer> templateLastRowMap) {
this.templateLastRowMap = templateLastRowMap;
}
@Override
public HolderEnum holderType() {
return HolderEnum.WORKBOOK;
......
......@@ -7,9 +7,13 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.converters.DefaultConverterLoader;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.CellRange;
import com.alibaba.excel.metadata.Head;
......@@ -48,8 +52,16 @@ public class ExcelWriteHeadProperty extends ExcelHeadProperty {
columnWidth = parentColumnWidth;
}
headData.setColumnWidthProperty(ColumnWidthProperty.build(columnWidth));
}
// If have @NumberFormat, 'NumberStringConverter' is specified by default
if (excelContentPropertyData.getConverter() == null) {
NumberFormat numberFormat = field.getAnnotation(NumberFormat.class);
if (numberFormat != null) {
excelContentPropertyData.setConverter(DefaultConverterLoader.loadAllConverter()
.get(ConverterKeyBuild.buildKey(field.getType(), CellDataTypeEnum.STRING)));
}
}
}
}
public RowHeightProperty getHeadRowHeightProperty() {
......
......@@ -42,14 +42,14 @@ public class TemplateDataTest {
EasyExcel.write(file, TemplateData.class)
.withTemplate(TestFileUtil.readFile("template" + File.separator + "template07.xlsx")).sheet()
.doWrite(data());
EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(2).sheet().doRead();
EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(3).sheet().doRead();
}
private void readAndWrite03(File file) {
EasyExcel.write(file, TemplateData.class)
.withTemplate(TestFileUtil.readFile("template" + File.separator + "template03.xls")).sheet()
.doWrite(data());
EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(2).sheet().doRead();
EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(3).sheet().doRead();
}
private List<TemplateData> data() {
......
package com.alibaba.easyexcel.test.demo.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 PoiTest {
private static final Logger LOGGER = LoggerFactory.getLogger(PoiTest.class);
@Test
public void lastRowNum() throws IOException {
String file = TestFileUtil.getPath() + "demo" + File.separator + "demo.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);
xssfSheet.createRow(20);
LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum());
}
@Test
public void lastRowNumXSSF() throws IOException {
String file = TestFileUtil.getPath() + "demo" + File.separator + "demo.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());
}
}
......@@ -13,6 +13,8 @@ import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.converters.DefaultConverterLoader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSON;
......@@ -50,7 +52,10 @@ public class ReadTest {
/**
* 指定列的下标或者列名
* <li>使用{@link com.alibaba.excel.annotation.ExcelProperty}注解即可
*
* <li>1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData}
* <li>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener}
* <li>3. 直接读即可
*/
@Test
public void indexOrNameRead() {
......@@ -78,10 +83,10 @@ public class ReadTest {
}
/**
* 日期、数字或者自定义格式转换
* 日期、数字或者自定义格式转换
* <p>
* 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()}
* <li>1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解.
* <li>1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解
* <li>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener}
* <li>3. 直接读即可
*/
......@@ -98,7 +103,7 @@ public class ReadTest {
}
/**
* 多行头.
* 多行头
*
* <li>1. 创建excel对应的实体对象 参照{@link DemoData}
* <li>2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
......@@ -134,7 +139,6 @@ public class ReadTest {
Map<Integer, String> data = (Map<Integer, String>)obj;
LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
}
}
}
......@@ -26,7 +26,7 @@ public class WebTest {
* 文件下载
* <li>1. 创建excel对应的实体对象 参照{@link DownloadData}
* <li>2. 设置返回的 参数
* <li>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭异常问题不大
* <li>3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
......
package com.alibaba.easyexcel.test.demo.write;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 复杂头数据.这里最终效果是第一行就一个主标题,第二行分类
*
* @author Jiaju Zhuang
**/
@Data
public class ComplexHeadData {
@ExcelProperty({"主标题", "字符串标题"})
private String string;
@ExcelProperty({"主标题", "日期标题"})
private Date date;
@ExcelProperty({"主标题", "数字标题"})
private Double doubleData;
}
package com.alibaba.easyexcel.test.demo.write;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.Data;
/**
* 基础数据类.这里的排序和excel里面的排序一致
*
* @author Jiaju Zhuang
**/
@Data
public class ConverterData {
/**
* 我想所有的 字符串起前面加上"自定义:"三个字
*/
@ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class)
private String string;
/**
* 我想写到excel 用年月日的格式
*/
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty("日期标题")
private Date date;
/**
* 我想写到excel 用百分比表示
*/
@NumberFormat("#.##%")
@ExcelProperty(value = "数字标题")
private Double doubleData;
}
package com.alibaba.easyexcel.test.demo.write;
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;
/**
* String and string converter
*
* @author Jiaju Zhuang
*/
public class CustomStringStringConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 这里是读的时候会调用 不用管
*
* @param cellData
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return cellData.getStringValue();
}
/**
* 这里是写的时候会调用 不用管
*
* @param value
* NotNull
* @param contentProperty
* Nullable
* @param globalConfiguration
* NotNull
* @return
*/
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData("自定义:" + value);
}
}
package com.alibaba.easyexcel.test.demo.write;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 基础数据类
*
* @author Jiaju Zhuang
**/
@Data
public class IndexData {
@ExcelProperty(value = "字符串标题", index = 0)
private String string;
@ExcelProperty(value = "日期标题", index = 1)
private Date date;
/**
* 这里设置3 会导致第二列空的
*/
@ExcelProperty(value = "数字标题", index = 3)
private Double doubleData;
}
package com.alibaba.easyexcel.test.demo.write;
import java.util.Date;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;
/**
* 基础数据类
*
* @author Jiaju Zhuang
**/
@Data
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class WidthAndHeightData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
/**
* 宽度为50
*/
@ColumnWidth(50)
@ExcelProperty("数字标题")
private Double doubleData;
}
package com.alibaba.easyexcel.test.demo.write;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.junit.Ignore;
import org.junit.Test;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
/**
* 写的常见写法
......@@ -21,19 +35,19 @@ import com.alibaba.excel.write.metadata.WriteSheet;
public class WriteTest {
/**
* 最简单的写
* <li>1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
* <li>1. 创建excel对应的实体对象 参照{@link DemoData}
* <li>2. 直接写即可
*/
@Test
public void simpleWrite() {
// 写法1
String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcelFactory.write(fileName, DemoData.class).sheet("模板").doWrite(data());
// 写法2
fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读
ExcelWriter excelWriter = EasyExcelFactory.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcelFactory.writerSheet("模板").build();
......@@ -42,12 +56,175 @@ public class WriteTest {
excelWriter.finish();
}
/**
* 指定写入的列
* <li>1. 创建excel对应的实体对象 参照{@link IndexData}
* <li>2. 使用{@link ExcelProperty}注解指定写入的列
* <li>3. 直接写即可
*/
@Test
public void indexWrite() {
String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, IndexData.class).sheet("模板").doWrite(data());
}
/**
* 复杂头写入 指定写入的列
* <li>1. 创建excel对应的实体对象 参照{@link ComplexHeadData}
* <li>2. 使用{@link ExcelProperty}注解指定复杂的头
* <li>3. 直接写即可
*/
@Test
public void complexHeadWrite() {
String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data());
}
/**
* 重复多次写入
* <li>1. 创建excel对应的实体对象 参照{@link ComplexHeadData}
* <li>2. 使用{@link ExcelProperty}注解指定复杂的头
* <li>3. 直接调用二次写入即可
*/
@Test
public void repeatedWrite() {
String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读
ExcelWriter excelWriter = EasyExcelFactory.write(fileName, DemoData.class).build();
WriteSheet writeSheet = EasyExcelFactory.writerSheet("模板").build();
// 第一次写入会创建头
excelWriter.write(data(), writeSheet);
// 第二次写入会在上一次写入的最后一行后面写入
excelWriter.write(data(), writeSheet);
/// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
/**
* 日期、数字或者自定义格式转换。
* <li>1. 创建excel对应的实体对象 参照{@link ConverterData}
* <li>2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解
* <li>3. 直接写即可
*/
@Test
public void converterWrite() {
String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, ConverterData.class).sheet("模板").doWrite(data());
}
/**
* 指定写入的列
* <li>1. 创建excel对应的实体对象 参照{@link IndexData}
* <li>2. 使用{@link ExcelProperty}注解指定写入的列
* <li>3. 使用withTemplate 读取模板
* <li>4. 直接写即可
*/
@Test
public void templateWrite() {
String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, DemoData.class).withTemplate(templateFileName).sheet().doWrite(data());
}
/**
* 列宽、行高
* <li>1. 创建excel对应的实体对象 参照{@link WidthAndHeightData}
* <li>2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度
* <li>3. 直接写即可
*/
@Test
public void widthAndHeightWrite() {
String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data());
}
/**
* 自定义样式
* <li>1. 创建excel对应的实体对象 参照{@link DemoData}
* <li>2. 创建一个style策略 并注册
* <li>3. 直接写即可
*/
@Test
public void styleWrite() {
String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx";
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景设置为红色
headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short)20);
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 背景绿色
contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short)20);
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板")
.doWrite(data());
}
/**
* 合并单元格
* <li>1. 创建excel对应的实体对象 参照{@link DemoData}
* <li>2. 创建一个merge策略 并注册
* <li>3. 直接写即可
*/
@Test
public void mergeWrite() {
String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";
// 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
// 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcelFactory.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板")
.doWrite(data());
}
/**
* 使用table去写入
* <li>1. 创建excel对应的实体对象 参照{@link DemoData}
* <li>2. 然后写入table即可
*/
@Test
public void tableWrite() {
String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx";
// 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案例
// 这里 需要指定写用哪个class去读
ExcelWriter excelWriter = EasyExcelFactory.write(fileName, DemoData.class).build();
// 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了
WriteSheet writeSheet = EasyExcelFactory.writerSheet("模板").needHead(Boolean.FALSE).build();
// 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要
WriteTable writeTable0 = EasyExcelFactory.writerTable(0).needHead(Boolean.TRUE).build();
WriteTable writeTable1 = EasyExcelFactory.writerTable(1).needHead(Boolean.TRUE).build();
// 第一次写入会创建头
excelWriter.write(data(), writeSheet, writeTable0);
// 第二次写如也会创建头,然后在第一次的后面写入数据
excelWriter.write(data(), writeSheet, writeTable1);
/// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + 0);
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
......
......@@ -3,6 +3,8 @@
* 优化读写对外接口
* 加入转换器,方便格式转换
* 极大优化读大文件的内存和效率
* sheetNo 改成0开始
* 读支持指定列名
# 1.2.4
修复read()方法存在的bug
# 1.2.1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册