官方文档(完整功能展示):
地址
依赖
<!-- easyexcel导入导出 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.1</version>
</dependency>
导出
1.随便一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@ExcelProperty("用户id")
private Long uid;
@ExcelProperty("用户名")
private String uname;
@ExcelProperty("用户性别")
private int usex;
@ExcelProperty("注册时间")
private Date udate;
}
2.编一个方法用于返回要导出的数据
//编数据用作导出
private List<User> data() {
List<User> list = ListUtils.newArrayList();
Random random = new Random();
for (long i = 0; i < 10; i++) {
User data = new User(i,"用户"+i,random.nextInt(2),new Date());
list.add(data);
}
return list;
}
3.1 用户访问接口时进行下载
@GetMapping(value = "ceshi")
public void ceshi(HttpServletResponse response, @Param("fileName") String fileName) throws IOException {
ServletOutputStream out = response.getOutputStream();
response.setContentType("multipart/form-data");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
EasyExcel.write(out, User.class).sheet("模板").doWrite( data() );//User.class是要到出数据的实体类,data()是要导出的数据
out.flush();//使用户下载文件
}
3.2 直接规定一个地址进行保存
@GetMapping(value = "ceshi")
public void ceshi(){
String fileName = "E:\\ceshi\\" + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
EasyExcel.write(fileName, User.class).sheet("模板").doWrite(data());
}
请求接口方法:
链接
我这里是直接浏览器输入url访问的
测试3.1,效果
测试3.2,效果
导入
1.随便一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@ExcelProperty("用户id")
private Long uid;
@ExcelProperty("用户名")
private String uname;
@ExcelProperty("用户性别")
private int usex;
@ExcelProperty("注册时间")
private Date udate;
}
2.一个dao用于存储至数据库,此处随便写的用于演示
public class DemoDAO {
public void save(List<User> list){
System.out.println("调用-------------------------");
list.forEach(x->{
System.out.println(x);
});
}
}
3.官网复制的监听类
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
// 有个很重要的点 UserListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class UserListener implements ReadListener<User> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
/**
* 缓存的数据
*/
private List<User> userDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public UserListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public UserListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(User data, AnalysisContext context) {
log.info("解析到一条数据:{}");
userDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (userDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
userDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", userDataList.size());
demoDAO.save(userDataList);
log.info("存储数据库成功!");
}
}
4.测试接口
@GetMapping(value = "ceshi1")
public void ceshi1() {
String fileName="E:\\ceshi\\1678090270171.xlsx";
EasyExcel.read(fileName, User.class, new UserListener()).sheet().doRead();
}
第一个参数也可是输入流,如multipartFile.getInputStream()
效果
对于需要字典的字段(基本数据类型封装类写法)
1.修改实体类
@ExcelProperty(value = "用户性别",converter = UsexConverter.class)
private Integer usex;
2.类型装换器
public class UsexConverter implements Converter<Integer> {
@Override
public Class<?> supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
// 导入时字符串转换数字
switch (cellData.getStringValue()){
case "男" : return 1;
case "女" : return 0;
default: return -1;
}
}
/**
* 将从数据库中查到的数据转换为 Excel 展示的数据
* @param value 枚举对象
*/
@Override
public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
String sexName;
switch (value){
case 0 : sexName= "女";break;
case 1 : sexName= "男";break;
default: sexName="其他";
}
return new WriteCellData<>(sexName);
}
}
导出效果
导入效果
对于需要字典的字段(枚举写法)
1.添加枚举类
public enum UsexEnum {
MAN("男",1),WOMEN("女",0),UNKNOWN("未知",-1);
String name;
int value;
UsexEnum(String name,int value){
this.name = name;
this.value = value;
}
/*
根据name值返回枚举类
*/
public static UsexEnum getUsexEnumofName(String name){
for (UsexEnum us : UsexEnum.values())
if (name.equals(us.getName()))
return us;
return UNKNOWN;
}
String getName(){
return name;
}
int getValue(){
return value;
}
@Override
public String toString(){
return name;
}
}
2.修改实体类
@ExcelProperty(value = "用户性别",converter = UsexConverter.class)//UsexConverter为类型装换器
private UsexEnum usex;
3.类型装换器
package Ceshi.daorudaochu;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
/**
* Excel 性别类型装换器
*
* @author wang suo
* @version 1.0
* @date 2021/9/14 15:06
*/
@Slf4j
public class UsexConverter implements Converter<UsexEnum> {
@Override
public Class<?> supportJavaTypeKey() {
return UsexEnum.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
/**
*导入触发
* @param value 枚举对象
*/
@Override
public UsexEnum convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
// 导入时根据表格name值转化为枚举对象
return UsexEnum.getUsexEnumofName(cellData.getStringValue());
}
/**
* 导出触发,将从数据库中查到的数据转换为 Excel 展示的数据
* @param value 枚举对象
*/
@Override
public WriteCellData<?> convertToExcelData(UsexEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
// 将枚举类型按照 key 传值
return new WriteCellData<UsexEnum>(String.valueOf(value));
}
}
导出效果
导入效果(里边是空时没触发类型转换)
导出时添加下拉选项
1.添加自定义拦截器
public class CustomSheetWriteHandler implements SheetWriteHandler {
@Override
public void afterSheetCreate(SheetWriteHandlerContext context) {
// 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行
CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 0, 2, 2);//四个参数分别为起始行号,终止行号,起始列号,终止列号。均从0开始
DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper();
DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"男", "女"});//下拉框值
DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);
context.getWriteSheetHolder().getSheet().addValidationData(dataValidation);
}
}
2.导出时添加(此处为链式操作,可添加多个拦截器)
.registerWriteHandler(new CustomSheetWriteHandler())
效果
导出多个工作表
ExcelWriter excelWriter = EasyExcel.write("E:\\ceshi\\1678090270171.xlsx").build();
//具体工作表
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").head(xxx.class).build();//head根据实体类确定表头,不加就直接填充数据
excelWriter.write(result,writeSheet);//向表填充数据
writeSheet = EasyExcel.writerSheet("表2").build();//writerSheet随便起,不是指定要填哪个表而是第二个表叫表2
excelWriter.write(result2,writeSheet);
writeSheet = EasyExcel.writerSheet("Sheet3").build();//有几个writeSheet最后的xlsx就有几个工作表,sheel顺序按照代码顺序
excelWriter.write(result3,writeSheet);
excelWriter.finish();//关闭流(必须)