参考: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可以构造方法传进去@Slf4jpublic 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*/@Overridepublic void invoke(Users data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();log.info("所有数据解析完成!");}/*** 自定义处理数据的方法加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", cachedDataList.size());//这个去处理数据保存,还是干嘛//.......................log.info(cachedDataList.toString());log.info("存储数据库成功!");}}
第三步、开始读取
@Testpublic 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. 直接读即可*/@Testpublic 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实体类指定导出名称
第二步、获取数据直接导出
@Autowiredprivate UsersService usersService;@Testpublic 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);}
@Testpublic 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-8response.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-8response.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.0try (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();}}
根据模版填充表格
单组数据填充
模板
最终效果

@Testpublic 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*/@Testpublic void complexFill() {// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替// {} 代表普通变量 {.} 代表是list的变量//文件保存路径String fileName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";//获取模板的文件路径String templateFileName = "template/users.xlsx";// 方案1 : 使用 try-with-resources @since 3.1.0try (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);}}
模板
最终效果

