前言

easyexcel的github地址为:https://github.com/alibaba/easyexcel,目前的最新版本是v2.2.10。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模式。在上层做了模型转换的封装,让使用者更加简单方便。easyexcel可以在64M内存1分钟内读取75M(46W行25列)的Excel,当然还有急速模式能更快,但是内存占用会在100M多一点。

easyexcel和poi的maven依赖

easyexcel的maven依赖

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version></dependency>

poi的maven依赖

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.17</version></dependency><dependency><groupId>org.apche.poi</groupId><artifactId>poi</artifactId><version>3.17</version></dependency>

导入Excel文件

https://github.com/alibaba/easyexcel/tree/v2.2.10/src/test/java/com/alibaba/easyexcel/test/demo/read

DemoData.java

package com.alibaba.easyexcel.test.demo.read;import java.util.Date;import lombok.Data;/*** 基础数据类.这里的排序和excel里面的排序一致** @author Jiaju Zhuang**/
@Data
public class DemoData {private String string;private Date date;private Double doubleData;
}

DemoDataListener.java

package com.alibaba.easyexcel.test.demo.read;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;/*** 模板的读取类** @author Jiaju Zhuang*/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoDAO();}/*** 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来** @param demoDAO*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;}/*** 这个每一条数据解析都会来调用** @param data*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listlist.clear();}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();LOGGER.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {LOGGER.info("{}条数据,开始存储数据库!", list.size());demoDAO.save(list);LOGGER.info("存储数据库成功!");}
}

ReadTest.java

package com.alibaba.easyexcel.test.demo.read;import java.io.File;
import java.util.List;
import java.util.Map;import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
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.enums.CellExtraTypeEnum;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSON;/*** 读的常见写法** @author Jiaju Zhuang*/
@Ignore
public class ReadTest {private static final Logger LOGGER = LoggerFactory.getLogger(ReadTest.class);/*** 最简单的读* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>* 3. 直接读即可*/@Testpublic void simpleRead() {// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去// 写法1:String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();// 写法2:fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";ExcelReader excelReader = null;try {excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();ReadSheet readSheet = EasyExcel.readSheet(0).build();excelReader.read(readSheet);} finally {if (excelReader != null) {// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();}}}/*** 指定列的下标或者列名** <p>* 1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener}* <p>* 3. 直接读即可*/@Testpublic void indexOrNameRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里默认读取第一个sheetEasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();}/*** 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>* 3. 直接读即可*/@Testpublic void repeatedRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 读取全部sheet// 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();// 读取部分sheetfileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";ExcelReader excelReader = null;try {excelReader = EasyExcel.read(fileName).build();// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的ListenerReadSheet readSheet1 =EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();ReadSheet readSheet2 =EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能excelReader.read(readSheet1, readSheet2);} finally {if (excelReader != null) {// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();}}}/*** 日期、数字或者自定义格式转换* <p>* 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()}* <p>* 1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener}* <p>* 3. 直接读即可*/@Testpublic void converterRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())// 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。// 如果就想单个字段使用请使用@ExcelProperty 指定converter// .registerConverter(new CustomStringStringConverter())// 读取sheet.sheet().doRead();}/*** 多行头** <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}* <p>* 3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数,* 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。*/@Testpublic void complexHeaderRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()// 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行.headRowNumber(1).doRead();}/*** 读取表头数据** <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}* <p>* 3. 直接读即可*/@Testpublic void headerRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();}/*** 额外信息(批注、超链接、合并单元格信息读取)* <p>* 由于是流式读取,没法在读取到单元格数据的时候直接读取到额外信息,所以只能最后通知哪些单元格有哪些额外信息** <p>* 1. 创建excel对应的实体对象 参照{@link DemoExtraData}* <p>* 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExtraListener}* <p>* 3. 直接读即可** @since 2.2.0-beat1*/@Testpublic void extraRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "extra.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, DemoExtraData.class, new DemoExtraListener())// 需要读取批注 默认不读取.extraRead(CellExtraTypeEnum.COMMENT)// 需要读取超链接 默认不读取.extraRead(CellExtraTypeEnum.HYPERLINK)// 需要读取合并单元格信息 默认不读取.extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();}/*** 读取公式和单元格类型** <p>* 1. 创建excel对应的实体对象 参照{@link CellDataReadDemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}* <p>* 3. 直接读即可** @since 2.2.0-beat1*/@Testpublic void cellDataRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead();}/*** 数据转换等异常处理** <p>* 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener}* <p>* 3. 直接读即可*/@Testpublic void exceptionRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead();}/*** 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面*/@Testpublic void synchronousRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finishList<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();for (DemoData data : list) {LOGGER.info("读取到数据:{}", JSON.toJSONString(data));}// 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finishList<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();for (Map<Integer, String> data : listMap) {// 返回每条数据的键值对 表示所在的列 和所在列的值LOGGER.info("读取到数据:{}", JSON.toJSONString(data));}}/*** 不创建对象的读*/@Testpublic void noModelRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";// 这里 只要,然后读取第一个sheet 同步读取会自动finishEasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead();}
}

导出Excel文件

package com.alibaba.easyexcel.test.demo.write;import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.junit.Ignore;
import org.junit.Test;import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.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.util.FileUtils;
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;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;/*** 写的常见写法** @author Jiaju Zhuang*/
@Ignore
public class WriteTest {/*** 最简单的写* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 直接写即可*/@Testpublic void simpleWrite() {// 写法1String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());// 写法2fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = null;try {excelWriter = EasyExcel.write(fileName, DemoData.class).build();WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(data(), writeSheet);} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}}/*** 根据参数只导出指定列* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 根据自己或者排除自己需要的列* <p>* 3. 直接写即可** @since 2.1.1*/@Testpublic void excludeOrIncludeWrite() {String fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";// 根据用户传入字段 假设我们要忽略 dateSet<String> excludeColumnFiledNames = new HashSet<String>();excludeColumnFiledNames.add("date");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板").doWrite(data());fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";// 根据用户传入字段 假设我们只要导出 dateSet<String> includeColumnFiledNames = new HashSet<String>();includeColumnFiledNames.add("date");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板").doWrite(data());}/*** 指定写入的列* <p>* 1. 创建excel对应的实体对象 参照{@link IndexData}* <p>* 2. 使用{@link ExcelProperty}注解指定写入的列* <p>* 3. 直接写即可*/@Testpublic void indexWrite() {String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data());}/*** 复杂头写入* <p>* 1. 创建excel对应的实体对象 参照{@link ComplexHeadData}* <p>* 2. 使用{@link ExcelProperty}注解指定复杂的头* <p>* 3. 直接写即可*/@Testpublic void complexHeadWrite() {String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data());}/*** 重复多次写入* <p>* 1. 创建excel对应的实体对象 参照{@link ComplexHeadData}* <p>* 2. 使用{@link ExcelProperty}注解指定复杂的头* <p>* 3. 直接调用二次写入即可*/@Testpublic void repeatedWrite() {// 方法1 如果写到同一个sheetString fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";ExcelWriter excelWriter = null;try {// 这里 需要指定写用哪个class去写excelWriter = EasyExcel.write(fileName, DemoData.class).build();// 这里注意 如果同一个sheet只要创建一次WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来for (int i = 0; i < 5; i++) {// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}// 方法2 如果写到不同的sheet 同一个对象fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";try {// 这里 指定文件excelWriter = EasyExcel.write(fileName, DemoData.class).build();// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}// 方法3 如果写到不同的sheet 不同的对象fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";try {// 这里 指定文件excelWriter = EasyExcel.write(fileName).build();// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(DemoData.class).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}}/*** 日期、数字或者自定义格式转换* <p>* 1. 创建excel对应的实体对象 参照{@link ConverterData}* <p>* 2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解* <p>* 3. 直接写即可*/@Testpublic void converterWrite() {String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data());}/*** 图片导出* <p>* 1. 创建excel对应的实体对象 参照{@link ImageData}* <p>* 2. 直接写即可*/@Testpublic void imageWrite() throws Exception {String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx";// 如果使用流 记得关闭InputStream inputStream = null;try {List<ImageData> list = new ArrayList<ImageData>();ImageData imageData = new ImageData();list.add(imageData);String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg";// 放入五种类型的图片 实际使用只要选一种即可imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath)));imageData.setFile(new File(imagePath));imageData.setString(imagePath);inputStream = FileUtils.openInputStream(new File(imagePath));imageData.setInputStream(inputStream);imageData.setUrl(new URL("https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg"));EasyExcel.write(fileName, ImageData.class).sheet().doWrite(list);} finally {if (inputStream != null) {inputStream.close();}}}/*** 根据模板写入* <p>* 1. 创建excel对应的实体对象 参照{@link IndexData}* <p>* 2. 使用{@link ExcelProperty}注解指定写入的列* <p>* 3. 使用withTemplate 写取模板* <p>* 4. 直接写即可*/@Testpublic void templateWrite() {String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).withTemplate(templateFileName).sheet().doWrite(data());}/*** 列宽、行高* <p>* 1. 创建excel对应的实体对象 参照{@link WidthAndHeightData}* <p>* 2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度* <p>* 3. 直接写即可*/@Testpublic void widthAndHeightWrite() {String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data());}/*** 注解形式自定义样式* <p>* 1. 创建excel对应的实体对象 参照{@link DemoStyleData}* <p>* 3. 直接写即可** @since 2.2.0-beta1*/@Testpublic void annotationStyleWrite() {String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoStyleData.class).sheet("模板").doWrite(data());}/*** 拦截器形式自定义样式* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 创建一个style策略 并注册* <p>* 3. 直接写即可*/@Testpublic void handlerStyleWrite() {String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx";// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为红色headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());WriteFont headWriteFont = new WriteFont();headWriteFont.setFontHeightInPoints((short) 20);headWriteCellStyle.setWriteFont(headWriteFont);// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);// 背景绿色contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex());WriteFont contentWriteFont = new WriteFont();// 字体大小contentWriteFont.setFontHeightInPoints((short) 20);contentWriteCellStyle.setWriteFont(contentWriteFont);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板").doWrite(data());}/*** 合并单元格* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData} {@link DemoMergeData}* <p>* 2. 创建一个merge策略 并注册* <p>* 3. 直接写即可** @since 2.2.0-beta1*/@Testpublic void mergeWrite() {// 方法1 注解String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";// 在DemoStyleData里面加上ContentLoopMerge注解// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoMergeData.class).sheet("模板").doWrite(data());// 方法2 自定义合并单元格策略fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";// 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板").doWrite(data());}/*** 使用table去写入* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 然后写入table即可*/@Testpublic void tableWrite() {String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx";// 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案例// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = null;try {excelWriter = EasyExcel.write(fileName, DemoData.class).build();// 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了WriteSheet writeSheet = EasyExcel.writerSheet("模板").needHead(Boolean.FALSE).build();// 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).build();WriteTable writeTable1 = EasyExcel.writerTable(1).needHead(Boolean.TRUE).build();// 第一次写入会创建头excelWriter.write(data(), writeSheet, writeTable0);// 第二次写如也会创建头,然后在第一次的后面写入数据excelWriter.write(data(), writeSheet, writeTable1);} finally {// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}}}/*** 动态头,实时生成头写入* <p>* 思路是这样子的,先创建List<String>头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据** <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 然后写入table即可*/@Testpublic void dynamicHeadWrite() {String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx";EasyExcel.write(fileName)// 这里放入动态头.head(head()).sheet("模板")// 当然这里数据也可以用 List<List<String>> 去传入.doWrite(data());}/*** 自动列宽(不太精确)* <p>* 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 {@link LongestMatchColumnWidthStyleStrategy}重新实现.* <p>* poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。** <p>* 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData}* <p>* 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy}* <p>* 3. 直接写即可*/@Testpublic void longestMatchColumnWidthWrite() {String fileName =TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, LongestMatchColumnWidthData.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong());}/*** 下拉,超链接等自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个)* <p>* demo这里实现2点。1. 对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel 2. 对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 注册拦截器 {@link CustomCellWriteHandler} {@link CustomSheetWriteHandler}* <p>* 2. 直接写即可*/@Testpublic void customHandlerWrite() {String fileName = TestFileUtil.getPath() + "customHandlerWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).registerWriteHandler(new CustomSheetWriteHandler()).registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data());}/*** 插入批注* <p>* 1. 创建excel对应的实体对象 参照{@link DemoData}* <p>* 2. 注册拦截器 {@link CommentWriteHandler}* <p>* 2. 直接写即可*/@Testpublic void commentWrite() {String fileName = TestFileUtil.getPath() + "commentWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 这里要注意inMemory 要设置为true,才能支持批注。目前没有好的办法解决 不在内存处理批注。这个需要自己选择。EasyExcel.write(fileName, DemoData.class).inMemory(Boolean.TRUE).registerWriteHandler(new CommentWriteHandler()).sheet("模板").doWrite(data());}/*** 可变标题处理(包括标题国际化等)* <p>* 简单的说用List<List<String>>的标题 但是还支持注解* <p>* 1. 创建excel对应的实体对象 参照{@link ConverterData}* <p>* 2. 直接写即可*/@Testpublic void variableTitleWrite() {// 写法1String fileName = TestFileUtil.getPath() + "variableTitleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, ConverterData.class).head(variableTitleHead()).sheet("模板").doWrite(data());}/*** 不创建对象的写*/@Testpublic void noModelWrite() {// 写法1String fileName = TestFileUtil.getPath() + "noModelWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList());}private List<LongestMatchColumnWidthData> dataLong() {List<LongestMatchColumnWidthData> list = new ArrayList<LongestMatchColumnWidthData>();for (int i = 0; i < 10; i++) {LongestMatchColumnWidthData data = new LongestMatchColumnWidthData();data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i);data.setDate(new Date());data.setDoubleData(1000000000000.0);list.add(data);}return list;}private List<List<String>> variableTitleHead() {List<List<String>> list = new ArrayList<List<String>>();List<String> head0 = new ArrayList<String>();head0.add("string" + System.currentTimeMillis());List<String> head1 = new ArrayList<String>();head1.add("number" + System.currentTimeMillis());List<String> head2 = new ArrayList<String>();head2.add("date" + System.currentTimeMillis());list.add(head0);list.add(head1);list.add(head2);return list;}private List<List<String>> head() {List<List<String>> list = new ArrayList<List<String>>();List<String> head0 = new ArrayList<String>();head0.add("字符串" + System.currentTimeMillis());List<String> head1 = new ArrayList<String>();head1.add("数字" + System.currentTimeMillis());List<String> head2 = new ArrayList<String>();head2.add("日期" + System.currentTimeMillis());list.add(head0);list.add(head1);list.add(head2);return list;}private List<List<Object>> dataList() {List<List<Object>> list = new ArrayList<List<Object>>();for (int i = 0; i < 10; i++) {List<Object> data = new ArrayList<Object>();data.add("字符串" + i);data.add(new Date());data.add(0.56);list.add(data);}return list;}private List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}}

Web上传Excel文件(导入)和下载Excel文件(导出)

package com.alibaba.easyexcel.test.demo.web;import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;/*** web读写案例** @author Jiaju Zhuang**/
@Controller
public class WebTest {@Autowiredprivate UploadDAO uploadDAO;/*** 文件下载(失败了会返回一个有部分数据的Excel)* <p>* 1. 创建excel对应的实体对象 参照{@link DownloadData}* <p>* 2. 设置返回的 参数* <p>* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大*/@GetMapping("download")public void download(HttpServletResponse response) throws IOException {// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postmanresponse.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");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 会导致各种问题,请直接用浏览器或者用postmantry {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");// 这里需要设置不关闭流EasyExcel.write(response.getOutputStream(), DownloadData.class).autoCloseStream(Boolean.FALSE).sheet("模板").doWrite(data());} catch (Exception e) {// 重置responseresponse.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>* 1. 创建excel对应的实体对象 参照{@link UploadData}* <p>* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}* <p>* 3. 直接读即可*/@PostMapping("upload")@ResponseBodypublic String upload(MultipartFile file) throws IOException {EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();return "success";}private List<DownloadData> data() {List<DownloadData> list = new ArrayList<DownloadData>();for (int i = 0; i < 10; i++) {DownloadData data = new DownloadData();data.setString("字符串" + 0);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}
}

EasyExcel基本教程相关推荐

  1. easyexcel使用教程-导出篇

    easyExcel使用教程–导出篇 开始准备工作 1.导入Maven依赖 <dependency><groupId>com.alibaba</groupId>< ...

  2. EasyExcel全面教程快速上手

    EasyExcel教程 本文使用的技术是Alibaba集团开源的EasyExcel技术,该技术是针对Apache POI技术的封装和优化,主要解决了POI技术的耗内存问题,并且提供了较好的API使用. ...

  3. EasyExcel使用教程

    文章目录 一.EasyExcel简介 二.EasyExcel使用介绍 三.EasyExcel常用注解介绍 四.EasyExcel常见报错解决 一.EasyExcel简介 1.EasyExcel介绍:E ...

  4. EasyExcel详细教程

    目录 1.Excel导入导出的应用场景 2.EasyExcel特点 3.Excel进行写操作 4.Excel进行读操作 本篇文章为你详细介绍EasyExcel如何进行读取操作 1.Excel导入导出的 ...

  5. Springboot 下 EasyExcel 的数据导入导出

    文章目录 1.环境准备 1.0. excel数据 1.1. pom 1.2. excle映射实体 1.3. 自定义日期转换器 1.4.自定义异常 2. 数据导出 3. 数据导入 3.1. excel解 ...

  6. SpringBoot集成阿里EasyExcel导出excel高级实战

    目录 参考 一.引入包 二.导出到文件并输出到后台 三.过滤字段 方式1:类上加注解 @ExcelIgnoreUnannotated,过滤属性没有@ExcelProperty注解的字段 方式2:指定字 ...

  7. Jsoup:使用Java将爬虫得到的数据写入Excel,Jsoup得到的数据进行持久化,爬虫数据保存到本地Excel中

    Jsoup:使用Java将爬虫得到的数据写入Excel,Jsoup得到的数据进行持久化,爬虫数据保存到本地Excel中 一.资源 EasyExcel使用教程 Jsoup爬虫教程 二.代码 xml依赖 ...

  8. BAT 开发工具汇总

    BAT 程序员们常用的开发工具汇总 阿里篇 一.Java 线上诊断工具 Arthas 使用场景: 使用教程: 基础教程: 进阶教程: 开源地址: 二.IDE 插件 Cloud Toolkit 使用场景 ...

  9. Excel导入导出详细教程------EasyExcel功能整合

    最详细的教程------EasyExcel导入与导出功能整合 1.开发环境参数 name Value 开发工具 Intellij IDEA 2018 框架 SpringBoot(maven项目也可以) ...

最新文章

  1. redis字符串匹配_Redis的数据类型和抽象概念介绍
  2. 您有一份意外的福利待领取!
  3. Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)
  4. Scala - 快速学习08 - 函数式编程:高阶函数
  5. 【简洁代码】1028 List Sorting (25 分)_26行代码AC
  6. CentOS/RHEL安装oracle 11G
  7. 图解集成学习中的梯度提升思想
  8. java图片转换为数据流_OpenCV 读取数据流图片
  9. 祝贺!吴晟成为 Apache 软件基金会首位中国董事!
  10. 感觉前路任重而道远呀。
  11. 问题:Cannot assign a device for operation Variable
  12. linux常用命令技巧
  13. 错误解决办法:exec /usr/lib/ssh/ssh-askpass : No such file or directory error.
  14. 百年GE濒临破产,传奇霸业何以衰亡?
  15. 为什么visual的联机浏览功能不能用_Minecraft非正版联机教程
  16. 用友集团前端面经整理及答案
  17. python的pandas库无法调用_pandas库中最重要的几个知识点
  18. js需要删除页面中某个元素
  19. 番外4. Python OpenCV 中鼠标事件相关处理与常见问题解决方案
  20. Fruit (hdu 2152)

热门文章

  1. 从零开始的颜值评分 游戏项目 (一)Maven的相关配置 ——第一个SpringBoot小程序
  2. 什么是GMS、CDMA、GPRS、EDGE、WCDMA、TD-CDMA、HSPA+、LTE?
  3. 【软件质量】软件完整性
  4. python 163邮箱发送邮件
  5. 代码保护(一) 几款加壳工具
  6. TM1637数码管显示STC51单片机驱动程序
  7. raid卷构建实操(raid0、raid1、raid5、raid6以及raid10),可跟做
  8. 沁恒CH552 USB学习之旅(一)
  9. java部门管理_系统管理模块_部门管理_实现基本的增删改查功能
  10. python爬取电脑本地数据_利用python爬取丁香医生上新型肺炎数据,并下载到本地,附带经纬度信息...