你要的不固定列excel导入导出,它来啦!
看完本文,你一定会有所收获
一、介绍
在上篇文章中,我们简单的介绍了 excel 导入导出技术实践方案,就目前而已,使用最多的开源框架主要有以下三类,分别是:
- apache poi:poi是使用最广的一种导入导出框架,但是缺点也很明显,导出大数据量的时候,容易oom
- easypoi:easypoi 的底层也是基于 apache poi 进行深度开发的,它主要的特点就是将更多重复的工作,全部简单化,避免编写重复的代码,最显著的特点就是导出的支持非常丰富
- easyexcel:easyexcel 是阿里巴巴开源的一款 excel 解析工具,底层逻辑也是基于 apache poi 进行二次开发的,目前的应用也非常广
总的来说,easypoi 和 easyexcel 都是基于apache poi进行二次开发的。
不同点在于:
1、easypoi 在读写数据的时候,优先是先将数据写入内存,因此读写性能非常高,这种操作平时使用的时候不会出现什么问题,但是当数据量很大的时候,会出现 oom,当然它也提供了 sax 模式一行一行解析,需要自己根据当前场景来实现。
2、easyexcel 默认基于 sax 模式一行一行解析,明显降低了内存,不会出现 oom 情况,程序有过高并发场景的验证,因此整体运行比较稳定,相对于 easypoi 来说,读写性能稍慢!
3、easypoi 的 api 非常丰富,easyexcel 功能的支持,比较简单。
就小编的实际使用情况来看,easypoi 相比 easyexcel 而言,有很多的优点,尤其是他的 api 非常丰富,但是在实际使用过程中,发现在导入几千条数据的时候,有时容易发生异常,尤其是当老板使用的时候,突然蹦出这么一个异常,这个时候是没办法容忍的。
但是当改用成 easyexcel 的时候,不会出现这个问题,因此如果你经常要导入的数据量非常大,那么我推荐你使用 easyexcel。
今天,我们就以 easyexcel 框架为例,结合实际开发案例,给大家详细介绍一下 easyexcel 的使用,再下篇文章中,我们再来介绍 easypoi,可能也有理解不到位的地方,欢迎网友们批评指出!
二、程序实例
2.1、添加依赖包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
2.2、导出 excel
easyexcel 的导出支持两种方式,一种是通过实体类注解方式来生成文件,另一种是通过动态参数化生成文件。
2.2.1、实体类注解方式生成文件
实体类注解方式生成文件,操作非常简单,只需要在对应的属性字段上添加
@ExcelProperty
注解,然后填写列名,配置就完成了,示例代码如下:
public class UserEntity {
@ExcelProperty(value = "姓名")
private String name;
@ExcelProperty(value = "年龄")
private int age;
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "操作时间")
private Date time;
//set、get...
}
public static void main(String[] args) throws FileNotFoundException {
List<UserEntity> dataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
UserEntity userEntity = new UserEntity();
userEntity.setName("张三" + i);
userEntity.setAge(20 + i);
userEntity.setTime(new Date(System.currentTimeMillis() + i));
dataList.add(userEntity);
//定义文件输出位置
FileOutputStream outputStream = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user1.xlsx"));
EasyExcel.write(outputStream, UserEntity.class).sheet("用户信息").doWrite(dataList);
}
运行程序,打开文件内容结果!
2.2.2、动态参数化生成文件
动态参数化生成文件,这种方式小编使用的比较多,基于它,我们可以封装一个公共的导出工具类,在后面会单独介绍给大家,示例代码如下:
public static void main(String[] args) throws FileNotFoundException {
//定义表头
List<List<String>> headList = new ArrayList<>();
headList.add(Lists.newArrayList("姓名"));
headList.add(Lists.newArrayList("年龄"));
headList.add(Lists.newArrayList("操作时间"));
//定义数据体
List<List<Object>> dataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Object> data = new ArrayList<>();
data.add("张三" + i);
data.add(20 + i);
data.add(new Date(System.currentTimeMillis() + i));
dataList.add(data);
//定义文件输出位置
FileOutputStream outputStream = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user2.xlsx"));
EasyExcel.write(outputStream).head(headList).sheet("用户信息").doWrite(dataList);
}
运行程序,打开文件内容,结果与上面一致!
2.2.3、复杂表头的生成
很多时候我们需要导出的文件,表头比较复杂,例如,我们想导出如下图这样一个复杂表头,应该如何实现呢?
如果你是使用在实体类上添加注解方式生成文件,那么可以通过如下方式来实现:
public class UserEntity {
@ExcelProperty(value = "班级")
private String className;
@ExcelProperty({"学生信息", "姓名"})
private String name;
@ExcelProperty({"学生信息", "年龄"})
private int age;
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty({"学生信息", "入学时间"})
private Date time;
//set、get...
}
其中
{"学生信息", "姓名"}
这种表达式,表示在当前列,插入多行数据,第一行插入的是
学生信息
名称,第二行,插入的是
姓名
名称,因此形成多级表头!
如果你是使用的动态参数化生成文件,操作也同样类似,示例代码如下:
public static void main(String[] args) throws FileNotFoundException {
//定义多级表头
List<List<String>> headList = new ArrayList<>();
headList.add(Lists.newArrayList("班级"));
headList.add(Lists.newArrayList("学生信息", "姓名"));
headList.add(Lists.newArrayList("学生信息","年龄"));
headList.add(Lists.newArrayList("学生信息","入学时间"));
//定义数据体
List<List<Object>> dataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Object> data = new ArrayList<>();
data.add("一年级~1班");
data.add("张三" + i);
data.add(20 + i);
data.add(new Date(System.currentTimeMillis() + i));
dataList.add(data);
//定义文件输出位置
FileOutputStream outputStream = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user3.xlsx"));
EasyExcel.write(outputStream).head(headList).sheet("用户信息").doWrite(dataList);
}
其中
Lists.newArrayList("学生信息", "姓名")
表达的意思跟上面一样,在当前列下插入多行,类似于:
List<String> list = new ArrayList<>();
list.add("学生信息");
list.add("姓名");
Lists.newArrayList
编程来自于
guava
工具包!
2.2.4、自定义样式
在实际使用过程中,我们可能还需要针对文件做一下样式自定义,例如你想把表头设置为红色,内容设置为绿色,列宽、行宽都加大,应该如何实现呢?
操作也很简单,编写一个自定义样式类,然后在写入的时候注入进去。
/**
* 自定义样式
* @return
private static HorizontalCellStyleStrategy customerStyle(){
// 头的策略
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);
return horizontalCellStyleStrategy;
}
在写入的时候,将其注入,例如下面的动态导出:
//通过registerWriteHandler方法,将自定义的样式类注入进去
EasyExcel.write(outputStream).registerWriteHandler(customerStyle()).head(headList).sheet("用户信息").doWrite(dataList);
2.3、导入 excel
easyexcel 的导入同样也支持两种方式,和上面一样,一种是通过实体类注解方式来读取文件,另一种是通过动态监听器读取文件。
2.3.1、实体类注解方式来读取文件
实体类注解方式来读取文件时,要读取的 excel 表头需要与实体类一一对应,以下面的 excel 文件为例!
通过注解方式来读取,既可以指定列的下表,也可以通过列名来映射,但是两者只能取一个。
/**
* 读取实体类
public class UserReadEntity {
@ExcelProperty(value = "姓名")
private String name;
* 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
@ExcelProperty(index = 1)
private int age;
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "操作时间")
private Date time;
//set、get...
}
public static void main(String[] args) throws FileNotFoundException {
//同步读取文件内容
FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-user1.xls"));
List<UserReadEntity> list = EasyExcel.read(inputStream).head(UserReadEntity.class).sheet().doReadSync();
System.out.println(JSONArray.toJSONString(list));
}
运行程序,输出结果如下:
[{"age":20,"name":"张三0","time":1616920360000},{"age":21,"name":"张三1","time":1616920360000},{"age":22,"name":"张三2","time":1616920360000},{"age":23,"name":"张三3","time":1616920360000},{"age":24,"name":"张三4","time":1616920360000},{"age":25,"name":"张三5","time":1616920360000},{"age":26,"name":"张三6","time":1616920360000},{"age":27,"name":"张三7","time":1616920360000},{"age":28,"name":"张三8","time":1616920360000},{"age":29,"name":"张三9","time":1616920360000}]
2.3.2、动态监听器读取文件
动态监听器读取文件,与上面的方式有一个明显的区别是,我们需要重新写一个实现类,来监听 easyexcel 一行一行解析出来的数据,然后将数据封装出来,基于此,我们可以编写一套动态的导入工具类,详细工具类会下面介绍到,示例代码如下:
/**
* 创建一个监听器,继承自AnalysisEventListener
public class UserDataListener extends AnalysisEventListener<Map<Integer, String>> {
private static final Logger LOGGER = LoggerFactory.getLogger(UserDataListener.class);
* 表头数据
private List<Map<Integer, String>> headList = new ArrayList<>();
* 数据体
private List<Map<Integer, String>> dataList = new ArrayList<>();
* 这里会一行行的返回头
* @param headMap
* @param context
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context){
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
headList.add(headMap);
* 这个每一条数据解析都会来调用
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context){
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
dataList.add(data);
* 所有数据解析完成了 都会来调用
* @param context
@Override
public void doAfterAllAnalysed(AnalysisContext context){
LOGGER.info("所有数据解析完成!");
public List<Map<Integer, String>> getHeadList() {
return headList;
public List<Map<Integer, String>> getDataList() {
return dataList;
}
public static void main(String[] args) throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-user1.xls"));
//初始化一个监听器
UserDataListener userDataListener = new UserDataListener();
//读取文件数据
EasyExcel.read(inputStream, userDataListener).sheet().doRead();
System.out.println("表头:" + JSONArray.toJSONString(userDataListener.getHeadList()));
System.out.println("数据体:" + JSONArray.toJSONString(userDataListener.getDataList()));
}
运行程序,输出结果如下:
表头:[{0:"姓名",1:"年龄",2:"操作时间"}]
数据体:[{0:"张三0",1:"20",2:"2021-03-28 16:32:40"},{0:"张三1",1:"21",2:"2021-03-28 16:32:40"},{0:"张三2",1:"22",2:"2021-03-28 16:32:40"},{0:"张三3",1:"23",2:"2021-03-28 16:32:40"},{0:"张三4",1:"24",2:"2021-03-28 16:32:40"},{0:"张三5",1:"25",2:"2021-03-28 16:32:40"},{0:"张三6",1:"26",2:"2021-03-28 16:32:40"},{0:"张三7",1:"27",2:"2021-03-28 16:32:40"},{0:"张三8",1:"28",2:"2021-03-28 16:32:40"},{0:"张三9",1:"29",2:"2021-03-28 16:32:40"}]
其中
key
表示列下表!
2.3.3、复杂表头读取
在实际的开发中,我们还会遇到复杂表头的数据读取,以如下表头为例,我们应该如何读取呢?
如果你是采用注解的方式导出的文件,同样也可以通过注解方式来读取,例如上文中,我们是使用如下实体类生成的文件,我们也可通过这个类读取文件!
public class UserEntity {
@ExcelProperty(value = "班级")
private String className;
@ExcelProperty({"学生信息", "姓名"})
private String name;
@ExcelProperty({"学生信息", "年龄"})
private int age;
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty({"学生信息", "入学时间"})
private Date time;
//set、get
}
//读取文件
List<UserEntity> list = EasyExcel.read(filePath).head(UserEntity.class).sheet().doReadSync();
System.out.println(JSONArray.toJSONString(list));
读取结果如下:
[{"age":20,"className":"一年级~1班","name":"张三0","time":1618719961000},{"age":21,"className":"一年级~1班","name":"张三1","time":1618719961000},{"age":22,"className":"一年级~1班","name":"张三2","time":1618719961000},{"age":23,"className":"一年级~1班","name":"张三3","time":1618719961000},{"age":24,"className":"一年级~1班","name":"张三4","time":1618719961000},{"age":25,"className":"一年级~1班","name":"张三5","time":1618719961000},{"age":26,"className":"一年级~1班","name":"张三6","time":1618719961000},{"age":27,"className":"一年级~1班","name":"张三7","time":1618719961000},{"age":28,"className":"一年级~1班","name":"张三8","time":1618719961000},{"age":29,"className":"一年级~1班","name":"张三9","time":1618719961000}]
如果你是使用动态参数化来生成文件,那么这个时候可以采用动态监听器的方式来读取文件,在读取的时候需要指定数据所在行,示例代码如下:
public static void main(String[] args) throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-export-user4.xlsx"));
//初始化一个监听器
UserDataListener userDataListener = new UserDataListener();
//读取文件数据,指定数据所在行使用headRowNumber方法
EasyExcel.read(inputStream, userDataListener).sheet().headRowNumber(2).doRead();
System.out.println("表头:" + JSONArray.toJSONString(userDataListener.getHeadList()));
System.out.println("数据体:" + JSONArray.toJSONString(userDataListener.getDataList()));
}
读取结果如下:
表头:[{0:"班级",1:"学生信息",2:"学生信息",3:"学生信息"},{0:"班级",1:"姓名",2:"年龄",3:"入学时间"}]
数据体:[{0:"一年级~1班",1:"张三0",2:"20",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三1",2:"21",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三2",2:"22",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三3",2:"23",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三4",2:"24",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三5",2:"25",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三6",2:"26",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三7",2:"27",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三8",2:"28",3:"2021-04-18 12:26:01"},{0:"一年级~1班",1:"张三9",2:"29",3:"2021-04-18 12:26:01"}]
三、动态导出导入工具类封装
在实际使用开发中,我们不可能每来一个 excel 导入导出需求,就编写一个方法,而且很多业务需求都是动态导入导出,没办法基于实体类注解的方式来读取文件或者写入文件
因此,基于动态参数化生成文件和动态监听器读取文件方法,我们可以单独封装一套动态导出导出工具类,省的我们每次都需要重新编写大量重复工作,以下就是小编我在实际使用过程,封装出来的工具类,在此分享给大家!
- 动态导出工具类
public class DynamicEasyExcelExportUtils {
private static final Logger log = LoggerFactory.getLogger(DynamicEasyExcelExportUtils.class);
private static final String DEFAULT_SHEET_NAME = "sheet1";
* 动态生成导出模版(单表头)
* @param headColumns 列名称
* @return excel文件流
public static byte[] exportTemplateExcelFile(List<String> headColumns){
List<List<String>> excelHead = Lists.newArrayList();
headColumns.forEach(columnName -> { excelHead.add(Lists.newArrayList(columnName)); });
byte[] stream = createExcelFile(excelHead, new ArrayList<>());
return stream;
* 动态生成模版(复杂表头)
* @param excelHead 列名称
* @return
public static byte[] exportTemplateExcelFileCustomHead(List<List<String>> excelHead){
byte[] stream = createExcelFile(excelHead, new ArrayList<>());
return stream;
* 动态导出文件
* @param headColumnMap 有序列头部
* @param dataList 数据体
* @return
public static byte[] exportExcelFile(LinkedHashMap<String, String> headColumnMap, List<Map<String, Object>> dataList){
//获取列名称
List<List<String>> excelHead = new ArrayList<>();
if(MapUtils.isNotEmpty(headColumnMap)){
//key为匹配符,value为列名,如果多级列名用逗号隔开
headColumnMap.entrySet().forEach(entry -> {
excelHead.add(Lists.newArrayList(entry.getValue().split(",")));
List<List<Object>> excelRows = new ArrayList<>();
if(MapUtils.isNotEmpty(headColumnMap) && CollectionUtils.isNotEmpty(dataList)){
for (Map<String, Object> dataMap : dataList) {
List<Object> rows = new ArrayList<>();
headColumnMap.entrySet().forEach(headColumnEntry -> {
if(dataMap.containsKey(headColumnEntry.getKey())){
Object data = dataMap.get(headColumnEntry.getKey());
rows.add(data);
excelRows.add(rows);
byte[] stream = createExcelFile(excelHead, excelRows);
return stream;
* 生成文件
* @param excelHead
* @param excelRows
* @return
private static byte[] createExcelFile(List<List<String>> excelHead, List<List<Object>> excelRows){
try {
if(CollectionUtils.isNotEmpty(excelHead)){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
EasyExcel.write(outputStream).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.head(excelHead)
.sheet(DEFAULT_SHEET_NAME)
.doWrite(excelRows);
return outputStream.toByteArray();
} catch (Exception e) {
log.error("动态生成excel文件失败,headColumns:" + JSONArray.toJSONString(excelHead) + ",excelRows:" + JSONArray.toJSONString(excelRows), e);
return null;
* 导出文件测试
* @param args
* @throws IOException
public static void main(String[] args) throws IOException {
//导出包含数据内容的文件
LinkedHashMap<String, String> headColumnMap = Maps.newLinkedHashMap();
headColumnMap.put("className","班级");
headColumnMap.put("name","学生信息,姓名");
headColumnMap.put("sex","学生信息,性别");
List<Map<String, Object>> dataList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Map<String, Object> dataMap = Maps.newHashMap();
dataMap.put("className", "一年级");
dataMap.put("name", "张三" + i);
dataMap.put("sex", "男");
dataList.add(dataMap);
byte[] stream = exportExcelFile(headColumnMap, dataList);
FileOutputStream outputStream = new FileOutputStream(new File("/Users/panzhi/Documents/easyexcel-export-user5.xlsx"));
outputStream.write(stream);
outputStream.close();
}
- 动态导入工具类
/**
* 创建一个监听器
public class DynamicEasyExcelListener extends AnalysisEventListener<Map<Integer, String>> {
private static final Logger LOGGER = LoggerFactory.getLogger(UserDataListener.class);
* 表头数据(存储所有的表头数据)
private List<Map<Integer, String>> headList = new ArrayList<>();
* 数据体
private List<Map<Integer, String>> dataList = new ArrayList<>();
* 这里会一行行的返回头
* @param headMap
* @param context
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context){
LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
//存储全部表头数据
headList.add(headMap);
* 这个每一条数据解析都会来调用
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context){
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
dataList.add(data);
* 所有数据解析完成了 都会来调用
* @param context
@Override
public void doAfterAllAnalysed(AnalysisContext context){
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
LOGGER.info("所有数据解析完成!");
public List<Map<Integer, String>> getHeadList() {
return headList;
public List<Map<Integer, String>> getDataList() {
return dataList;
}
/**
* 编写导入工具类
public class DynamicEasyExcelImportUtils {
* 动态获取全部列和数据体,默认从第一行开始解析数据
* @param stream
* @return
public static List<Map<String,String>> parseExcelToView(byte[] stream) {
return parseExcelToView(stream, 1);
* 动态获取全部列和数据体
* @param stream excel文件流
* @param parseRowNumber 指定读取行
* @return
public static List<Map<String,String>> parseExcelToView(byte[] stream, Integer parseRowNumber) {
DynamicEasyExcelListener readListener = new DynamicEasyExcelListener();
EasyExcelFactory.read(new ByteArrayInputStream(stream)).registerReadListener(readListener).headRowNumber(parseRowNumber).sheet(0).doRead();
List<Map<Integer, String>> headList = readListener.getHeadList();
if(CollectionUtils.isEmpty(headList)){
throw new RuntimeException("Excel未包含表头");
List<Map<Integer, String>> dataList = readListener.getDataList();
if(CollectionUtils.isEmpty(dataList)){
throw new RuntimeException("Excel未包含数据");
//获取头部,取最后一次解析的列头数据
Map<Integer, String> excelHeadIdxNameMap = headList.get(headList.size() -1);
//封装数据体
List<Map<String,String>> excelDataList = Lists.newArrayList();
for (Map<Integer, String> dataRow : dataList) {
Map<String,String> rowData = new LinkedHashMap<>();
excelHeadIdxNameMap.entrySet().forEach(columnHead -> {
rowData.put(columnHead.getValue(), dataRow.get(columnHead.getKey()));
excelDataList.add(rowData);
return excelDataList;
* 文件导入测试
* @param args
* @throws IOException
public static void main(String[] args) throws IOException {
FileInputStream inputStream = new FileInputStream(new File("/Users/panzhi/Documents/easyexcel-export-user5.xlsx"));
byte[] stream = IoUtils.toByteArray(inputStream);
List<Map<String,String>> dataList = parseExcelToView(stream, 2);
System.out.println(JSONArray.toJSONString(dataList));
inputStream.close();
}
为了方便后续的操作流程,在解析数据的时候,会将列名作为
key
!
四、总结
本文主要以实际使用场景为例,对 easyexcel 的使用做了简单的介绍,尤其是动态导出导出,基于业务的需要,做了一个公共的工具类,方便后续进行快速开发,避免重复的劳动!
当然,easyexcel 的功能还不只上面介绍的那些内容,还有基于模版进行excel的填充,web 端restful的导出导出,使用方法大致都差不多,具体可以参与官方的文档,地址如下:
https://www.yuque.com/easyexcel/doc/read#1bfaf593
最后,希望本文对大家有所帮助!
五、参考
1、easyexcel - 接口文档
文章转载自公众号:Java极客技术
- 慢SQL夜袭数据库,妙手神医瑶池诊疗心法大公开 2023-08-23 15:05:29发布
- 数据库内核那些事|细说PolarDB优化器查询变换:IN-List变换 2023-08-23 15:04:40发布
直接抛异常,而且还不指明都需要import哪些包。请大家注意,这里的pom.xml文件引用的版本不能太高或者太低,否则就异常,请保持这个版本:
<!-- 阿里开源easyExcel依赖,不可修改版本号 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
异常信息如下:
14:16:47.749 [main] DEBUG com.alibaba.excel.metadata.property.ExcelHeadProperty - The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is NONE
14:16:47.834 [main] DEBUG com.alibaba.excel.context.AnalysisContextImpl - Initialization 'AnalysisContextImpl' complete
Exception in thread "main" com.alibaba.excel.exception.ExcelAnalysisException: java.lang.NoClassDefFoundError: org/apache/poi/util/POILogFactory
at com.alibaba.excel.analysis.ExcelAnalyserImpl.<init>(ExcelAnalyserImpl.java:61)
at com.alibaba.excel.ExcelReader.<init>(ExcelReader.java:30)
at com.alibaba.excel.read.builder.ExcelReaderBuilder.build(ExcelReaderBuilder.java:214)
at com.alibaba.excel.read.builder.ExcelReaderBuilder.sheet(ExcelReaderBuilder.java:251)
at com.alibaba.excel.read.builder.ExcelReaderBuilder.sheet(ExcelReaderBuilder.java:243)
at com.bba.aatf.application.common.utils.DynamicEasyExcelImportUtils.parseExcelToView(DynamicEasyExcelImportUtils.java:49)
at com.bba.aatf.application.common.utils.DynamicEasyExcelImportUtils.main(DynamicEasyExcelImportUtils.java:88)
Caused by: java.lang.NoClassDefFoundError: org/apache/poi/util/POILogFactory
at org.apache.poi.openxml4j.opc.PackagingURIHelper.<clinit>(PackagingURIHelper.java:41)
at com.alibaba.excel.analysis.v07.XlsxSaxAnalyser.<clinit>(XlsxSaxAnalyser.java:69)
at com.alibaba.excel.analysis.ExcelAnalyserImpl.choiceExcelExecutor(ExcelAnalyserImpl.java:103)
at com.alibaba.excel.analysis.ExcelAnalyserImpl.<init>(ExcelAnalyserImpl.java:55)
... 6 more
Caused by: java.lang.ClassNotFoundException: org.apache.poi.util.POILogFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 10 more
与目标 VM 断开连接, 地址为: ''127.0.0.1:55012',传输: '套接字''
进程已结束,退出代码1