github地址
https://github.com/alibaba/easyexcel
我用的springboot 照例先上依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>
需要的实体类和Listener
实体类,这里的@Data我用了idea Lombok的插件声明的get和set方法
package com.fchan.layui.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
public class EasyExcelTest implements Serializable {
private static final long serialVersionUID = 8362987561243233425L;
//@ExcelProperty(value ="类型",index = 0) //文档中建议只用 index 或者只用 value 不要混用
@ExcelProperty(index = 0)
private String type;//开支类型 信用卡等
@ExcelProperty(index =1)
private String sum;
@ExcelProperty(index =2)
private String name;//开支来源 如:**银行信用卡
@ExcelProperty(index =3)
private String date;
@ExcelProperty(index =4)
private Integer status;
@ExcelProperty(index =5)
private String descr;
}
对读取的excel中单元格格式为时间的内容进行格式化
要注意的一点就是实体类中接收的属性的类型只能为String才能格式化
Listener读取监听类
package com.fchan.layui.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fchan.layui.entity.EasyExcelTest;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
public class EasyExcelListener extends AnalysisEventListener<EasyExcelTest> {
static final ObjectMapper JSON = new ObjectMapper();
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
//Collections.synchronizedList给List的add方法加 synchronized 锁
List<EasyExcelTest> list = Collections.synchronizedList(new ArrayList<EasyExcelTest>());
/*
* 假设这个是一个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;
}
*/
/**
* 加上存储数据库
*/
private void saveData() {
//demoDAO.save(list);
log.info("存储数据库成功!");
}
/**
* 解析每一条数据的时候都会进这个方法
* @param data
* @param context
*/
@Override
public void invoke(EasyExcelTest data, AnalysisContext context) {
try {
log.info("解析到一条数据:{}",JSON.writeValueAsString(data));
if(list.size() >= BATCH_COUNT){
//存储数据库
saveData();
//然后准备攒下一波数据
list.clear();
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
/**
* 解析完成的时候也要保存一遍数据库
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if(list.size() > 0){
//存数据库
saveData();
}
}
}
读取demo
@PostMapping("testEasyExcel")
@ResponseBody
String testEasyExcel(@RequestParam("excel")MultipartFile excel) throws IOException {
//这个EasyExcelListener里已经写入了监听保存的操作,所以外面不需要自己再处理了
// 这里默认读取第一个sheet
EasyExcel.read(excel.getInputStream(), EasyExcelTest.class, new EasyExcelListener()).sheet().doRead();
return "success";
}
excel具有多行头解析demo
/**
* 多行头
*
* <p>1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数,
* 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。
*/
@Test
public void complexHeaderRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()
// 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行
.headRowNumber(1).doRead();
}
也可以获取读到的excel中的内容
不过这样会把数据加载到内存中
/**
* 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
*/
@Test
public void synchronousRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
for (DemoData data : list) {
LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
}
// 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
for (Map<Integer, String> data : listMap) {
// 返回每条数据的键值对 表示所在的列 和所在列的值
LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
}
}
导出excel Demo
@GetMapping("downExcel")
@ResponseBody
public void downExcel(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), EasyExcelTest.class).sheet("模板").doWrite(data());
}
public List<EasyExcelTest> data(){
List<EasyExcelTest> list = new ArrayList(){{
add(new EasyExcelTest("11","11","11","11",11,"11","11"));
add(new EasyExcelTest("22","22","22","22",22,"22","22"));
add(new EasyExcelTest("33","33","33","33",33,"33","33"));
}};
return list;
}
如果要修改导出的时候的第一行头的名称,可以用
@ExcelProperty(index =2,value = "开支来源")
注解指定列名
实体类demo
package com.fchan.layui.entity;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.fchan.layui.service.Test;
import lombok.Data;
import org.apache.commons.codec.binary.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Component
@Data
public class EasyExcelTest implements Serializable{
public EasyExcelTest() {
}
public EasyExcelTest(String type, String sum, String name, String date, Integer status, String descr, String beanName) {
this.type = type;
this.sum = sum;
this.name = name;
this.date = date;
this.status = status;
this.descr = descr;
this.beanName = beanName;
}
private static final long serialVersionUID = 8362987561243233425L;
//@ExcelProperty(value ="类型",index = 0) index代表第几列,从0开始
@ExcelProperty(index = 0,value = "开支类型")
private String type;//开支类型 信用卡等
@ExcelProperty(index =1,value = "总计")
private String sum;
@ExcelProperty(index =2,value = "开支来源")
private String name;//开支来源 如:**银行信用卡
/**
* 这里用string 去接日期才能格式化。我想接收年月日格式
*/
@ExcelProperty(index =3,value = "日期")
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") //代表从excel中读取到时间格式的单元格内容的时候格式化成 yyyy年MM月dd日HH时mm分ss秒
private String date;
@ExcelProperty(index =4,value = "状态")
private Integer status;
@ExcelProperty(index =5,value = "备注")
@NumberFormat("#.##%") //将接收的数字在读取转成这个 entity的status属性的值的时候转成百分比格式
private String descr;
@ExcelIgnore
private String beanName;
}
具体还是要看github上的官方文档
https://github.com/alibaba/easyexcel