参考:2小时快速掌握EasyExcel报表导入导出-黑马程序员杭州校区出品
https://www.cnblogs.com/yinrz/p/15272431.html
阿里官方使用说明
引入依赖
<!-- excel导入和导出插件-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.0</version>
</dependency>
读取excel表格文件方法
第一步、定义一个java实体类用来接收数据
第二步、设置监听器
package com.tj.demo.system.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.tj.demo.system.domain.Users;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
// 有个很重要的点 UsersListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class UsersListener implements ReadListener<Users> {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<Users> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private Users users;
public UsersListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
users = new Users();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param users
*/
public UsersListener(Users users) {
this.users = users;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(Users data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 自定义处理数据的方法加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
//这个去处理数据保存,还是干嘛
//.......................
log.info(cachedDataList.toString());
log.info("存储数据库成功!");
}
}
第三步、开始读取
@Test
public void testReadExcel() {
/*
获得一个工作簿读对象
参数一:Excel文件路径
参数二:每行数据对应的实体类型
参数三:读监听器,每读一行就会调用一次该监听器的invoke方法
*/
ExcelReaderBuilder readWorkBook = EasyExcel.read("C:/Users/心有灵茜/Desktop/新建文件夹/users.xls", Users.class, new UsersListener());
// 获得一个工作表对象
ExcelReaderSheetBuilder sheet = readWorkBook.sheet();
// 读取工作表中的内容
sheet.doRead();
}
/**
* 最简单的读(官方写法)
* 1. 创建excel对应的实体对象 参照{@link Users}
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UsersListener}
* 3. 直接读即可
*/
@Test
public void simpleRead() {
//文件名含路径
String fileName = "C:/Users/心有灵茜/Desktop/新建文件夹/users.xls";
// 有个很重要的点 UsersListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法3(选用官方的第三种简单写法):
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, Users.class, new UsersListener()).sheet().doRead();
}
导出excel表格
第一步、在java实体类指定导出名称
第二步、获取数据直接导出
@Autowired
private UsersService usersService;
@Test
public void testWriteExcel() {
/*
获得一个工作簿写对象
参数一:导出的Excel文件路径
参数二:每行数据对应的实体类型
*/
String pathName="C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
ExcelWriterBuilder writeWorkBook = EasyExcel.write(pathName, Users.class);
// 获得一个工作表对象
ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();
// 准备要导出的数据
List<Users> list = usersService.list();
// 写入工作表
sheet.doWrite(list);
}
@Test
public void simpleWriteExcel() {
/**
* 最简单的写
* 1. 创建excel对应的实体对象
* 2. 直接写即可
*/
String pathName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
// 准备要导出的数据
List<Users> list = usersService.list();
// 写法2
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(pathName, Users.class).sheet("sheet1").doWrite(list);
}
注解参数说明
//这个注解可以指定导出的表头名称和导入的表头名称
//value是表头名称,index是列的位置排序,index不设置,是默认的排序
@ExcelProperty(value="帐号",index=2)
//指定不导出的列
@ExcelIgnore()
//日期格式化
@DateTimeFormat("yyyy年MM月dd日")
//数字格式化
@NumberFormat
// 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
@ExcelIgnoreUnannotated
/**
*样式设置
*/
//设置导出的列宽@ColumnWidth(20)
@ColumnWidth(20)
//表头行高设定,要写在class类上,不是字段上
@HeadRowHeight(10)
//字段内容行高设定,要写在class类上,不是字段上
@ContentRowHeight(20)
自定义转换器
参考:EasyExcel导出-自定义转换器,将某个字段值转换成字符串
复杂表头设置
效果图
// @ExcelProperty的value是一个字符串数组,默认相同的会合并到一起
@ExcelProperty({"个人信息","帐号"})
private String user_accout;
@ExcelProperty({"个人信息","姓名"})
private String user_name;
Web上传和下载方法
下载二次封装
/**
* 通过EasyExcel导出表格文件(下载失败了会返回一个有部分数据的Excel)
* 1. 创建excel对应的实体对象
* 2. 设置返回的 参数
* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*
* @param response 响应输出
* @param head Java实体类
* @param data 导出data数据
* @throws IOException
*/
public static void downxlsxByEasyexcel(HttpServletResponse response, Class head, Collection<?> data) throws IOException {
//设置返回文件类型
response.setContentType("application/vnd.ms-excel");
//设置编码格式utf-8
response.setCharacterEncoding("utf-8");
//这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("导出xlsx-" + TjDateUtils.getTodayString(), "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), head).sheet("sheet1").doWrite(data);
}
用模版下载二次封装
/**
* 根据自己设置的模板文件,通过EasyExcel导出表格文件
*
* @param response 输出流
* @param templateFileName 模板文件名称
* @param data 数据
* @throws UnsupportedEncodingException
*/
public static void downxlsxByEasyexcelWithTemplate(HttpServletResponse response, String templateFileName, Collection<?> data) throws UnsupportedEncodingException {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
//设置返回文件类型
response.setContentType("application/vnd.ms-excel");
//设置编码格式utf-8
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("导出xlsx-" + TjDateUtils.getTodayString(), "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 方案1 : 使用 try-with-resources @since 3.1.0
try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
//填充数据
excelWriter.fill(data, fillConfig, writeSheet);
} catch (IOException e) {
e.printStackTrace();
}
}
根据模版填充表格
单组数据填充
模板
最终效果
@Test
public void testWriteWithTempalte() {
//文件保存路径
String pathName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
//获取模板的文件路径
String templatePath = "template/users.xlsx";
//创建一个工作簿对象
ExcelWriterBuilder writerBuilder = EasyExcel.write(pathName, Users.class).withTemplate(templatePath);
//创建工作表对象
ExcelWriterSheetBuilder sheet = writerBuilder.sheet();
//方法1,准备对象数据,使用对象
Users users = new Users();
users.setUser_accout("ce");
users.setUser_name("1111");
//方法2,准备Map数据
Map<String, Object> map = new HashMap<String, Object>();
map.put("user_accout", "张三");
map.put("user_name", 5.2);
//填充数据
sheet.doFill(users);
}
多组数据填充列表
多行模板
复杂填充模板
/**
* 复杂的填充
*
* @since 2.1.1
*/
@Test
public void complexFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
//文件保存路径
String fileName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
//获取模板的文件路径
String templateFileName = "template/users.xlsx";
// 方案1 : 使用 try-with-resources @since 3.1.0
try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
// 如果数据量大 list不是最后一行 参照下一个
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
//准备数据
List<Users> list = usersService.list();
excelWriter.fill(list, fillConfig, writeSheet);
Map<String, Object> map = MapUtils.newHashMap();
map.put("date", "2019年10月9日13:28:28");
map.put("total", 1000);
excelWriter.fill(map, writeSheet);
}
}