前言
之前出过一期easyExcel在SSM的环境下使用流程,本篇演示在springboot环境下easyExcel的使用
上篇链接
使用EasyExcel实现表格的导入导出【http://t.csdn.cn/0MLgt】
一、导入依赖
<!-- easyexcel依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
二、配置监听器
package com.lzl.idpac.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.lzl.idpac.entity.Student;
import com.lzl.idpac.entity.StudentExcel;
import com.lzl.idpac.service.StudentService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* --效率,是成功的核心关键--
* 学生信息管理导入excel表格监听器
*
* @Author lzl
* @Date 2023/1/30 21:05
*/
@Component
@Scope("prototype")//标记此处的监听器为多例的,防止并发读操作时出现错误
public class StudentReadListener implements ReadListener<StudentExcel> {
@Autowired
private StudentService service;
//注入学生管理业务层接口
@Override
public void invoke(StudentExcel studentExcel, AnalysisContext analysisContext) {
//每读取一行,就调用一次,把每一行数据封装到实体类中
Student student = new Student();
BeanUtils.copyProperties(studentExcel,student);
//属性不一致时使用属性拷贝
//导入之前做一个去重判断
Integer key = service.findBySno(student.getStuNo());
if(key != 1 ){//数据库中没有该学号
service.addNew(student);
//调用新增方法
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//数据解析完毕执行方法
}
}
三、controller编写
package com.lzl.idpac.controller;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.lzl.idpac.entity.Student;
import com.lzl.idpac.entity.StudentExcel;
import com.lzl.idpac.listener.StudentReadListener;
import com.lzl.idpac.service.StudentService;
import com.lzl.idpac.utils.LayUiUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
/**
* --效率,是成功的核心关键--
* excel表格上传控制层
*
* @Author lzl
* @Date 2023/1/30 21:18
*/
@RestController
@RequestMapping("/excel")
public class ExcelController {
@Autowired
private StudentReadListener listener;//注入监听器
@Autowired
private StudentService service;//注入学生管理业务层
/**
*excel数据写入数据库
* @param file 获得前端上传的文件 EasyExcel.read 需要传入三个参数 文件流 操作实体类的字节码 监听器
* @return 0 成功上传
* @throws IOException
*/
@RequestMapping("/read")
@ResponseBody
public LayUiUtil<String> readExcel(MultipartFile file) throws IOException {
// 得到excel读取对象 //通过文件获得流, 获得读取文件的class 填入监听器 监听器每读取一行就执行一次新增
ExcelReaderBuilder read = EasyExcel.read(file.getInputStream(), StudentExcel.class, listener);
//获取表格
ExcelReaderSheetBuilder sheet = read.sheet();
//读取表格
sheet.doRead();
//设置容器
LayUiUtil<String> layUiUtil = new LayUiUtil<>();
//配置前端响应状态码
layUiUtil.setCode(0);
//配置服务器返回信息
layUiUtil.setMsg("已成功导入");
return layUiUtil;
}
/**
* 导出选中的数据到excel表格
* @param stuNos
* @param stuCollegeNo
* @param response
* @throws IOException
*/
@RequestMapping("/write")
public void writeExcel(String stuNos,String stuCollegeNo, HttpServletResponse response) throws IOException {
//设置响应头
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
//导出的文件名
String filename = URLEncoder.encode("学生信息","utf-8");
//设置响应头
response.setHeader("Content-Disposition","attachment;filename="+filename+".xlsx");
//获得流对象
ServletOutputStream outputStream = response.getOutputStream();
//获得write对象
ExcelWriterBuilder write = EasyExcel.write(outputStream, StudentExcel.class);
//获得数据表对象
ExcelWriterSheetBuilder sheet = write.sheet();
//准备需要输出的数据
List<StudentExcel> list = service.getExcelDataByNos(stuNos,stuCollegeNo);
//生成表格文件
sheet.doWrite(list);
}
}
进行导入操作时,也可以不配置监听器,通过匿名内部类实现,代码如下:
private StudentService service;//注入学生管理业务层
/**
*excel数据写入数据库
* @param file 获得前端上传的文件 EasyExcel.read 需要传入三个参数 文件流 操作实体类的字节码 监听器
* @return 0 成功上传
* @throws IOException
*/
@RequestMapping("/read")
@ResponseBody
public LayUiUtil<String> readExcel(MultipartFile file,String stuCollegeNo) throws IOException {
//匿名内部类
EasyExcel.read(file.getInputStream(), StudentExcel.class, new ReadListener<StudentExcel>() {
/**
* 单次缓存的数据量
*/
public static final int BATCH_COUNT = 100;
/**
*临时存储
*/
private List<StudentExcel> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Override
public void invoke(StudentExcel data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();//调用存储数据
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 读取完之后执行的方法
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
saveData();
}
/**
* 加上存储数据库
*/
private void saveData() {
for(StudentExcel studentExcel : cachedDataList) {
Student student = new Student();
if(service.findBySno(studentExcel.getStuNo()) != 1){
BeanUtils.copyProperties(studentExcel,student);
student.setStuCollegeNo(stuCollegeNo);
service.addNew(student);
}
}
}
}).sheet().doRead();
//第二种方法,采用监听器操作
// // 得到excel读取对象 //通过文件获得流, 获得读取文件的class 填入监听器 监听器每读取一行就执行一次新增
// ExcelReaderBuilder read = EasyExcel.read(file.getInputStream(), StudentExcel.class, listener);
// //获取表格
// ExcelReaderSheetBuilder sheet = read.sheet();
// //读取表格
// sheet.doRead();
//设置容器
LayUiUtil<String> layUiUtil = new LayUiUtil<>();
//配置前端响应状态码
layUiUtil.setCode(0);
//配置服务器返回信息
layUiUtil.setMsg("已成功导入");
return layUiUtil;
}
匿名内部类的方法虽然不如监听器那么简洁,但是可以让我们在controller层传入一些额外的参数,做一些别的操作。使用监听器显然是无法传入的。例如在本业务中,我在新增学生,需要同步给学生加一个学院id的属性,使用监听器无法传入学院id,但是使用匿名内部类却可以直接使用这个学院id。真实开发中这些业务逻辑还是要放在service层,此处只是学习之用,就无所谓了
四、Service业务层
接口
/**
* 根据学号,获取需要导出的数据
* @param stuNos 学号字串
* @return layui工具类 泛型StudentExcel
*/
List<StudentExcel> getExcelDataByNos(String stuNos,String stuCollegeNo);
/**
* 根据学号获得学生信息,用于导入去重判断
* @param stuNo
* @return 受影响行数
*/
Integer findBySno(String stuNo);
/**
*新增学生信息
* @param student 学生实体
* @return layui工具类 泛型Student
*/
LayUiUtil<Student> addNew(Student student);
业务实现
/**
* 根据学号,获取需要导出的数据
*
* @param stuNos
* @return
*/
@Override
public List<StudentExcel> getExcelDataByNos(String stuNos,String stuCollegeNo) {
//设置一个数组,存放需要导出的学号
ArrayList<String> params = new ArrayList<>();
if(stuNos != null && !stuNos.equals("")){//判断前端是否传递了学号
String[] strList = stuNos.split(",");
//将需要导出数据的学号遍历添加进集合
for (String s : strList) {
params.add(s);
}
}
//创建一个集合用来存放数据
List<Student> list = new ArrayList<Student>();
if(params.size()==0){
//如果前端没有传递学号,即为下载模板操作,需要捏造一个测试数据
list.add(new Student("202150915100","张三","男",18,"4102222000xxxxxxxx",null,
"计算机科学与技术2001","慧苑1号楼101","138xxxxxxx9",null));
}else {
//如果前端传了学号,则为导出操作,执行持久层,获取需要导出的数据
list = mapper.getStudentByStuNo(params);
}
//使用Lambda表达式把employee转换成employeeBo
List<StudentExcel> collect = list.stream()
.map((student)->{
StudentExcel studentExcel = new StudentExcel();
BeanUtils.copyProperties(student,studentExcel);
return studentExcel;
}).collect(Collectors.toList());
return collect;
}
/**
* 根据学号获得学生信息,用于导入去重判断
*
* @param stuNo
* @return 受影响行数
*/
@Override
public Integer findBySno(String stuNo) {
return mapper.findStudentByNo(stuNo);
}
/**
* 新增学生信息实现类
* @param student
* @return
*/
@Override
public LayUiUtil<Student> addNew(Student student) {
LayUiUtil<Student> layUiUtil = new LayUiUtil<>();
String IDcard = student.getStuIDcard();//获取身份证号码
int nums = IDcard.length()-6;//获取需要截取字串的下标位置
String password = student.getStuIDcard().substring(nums);//对字串进行切割获得密码
student.setStuPassword(password);//设置进实体类
Integer key = mapper.addNew(student);
if (key != 0){
layUiUtil.setCode(1);
layUiUtil.setMsg("新增成功!");
}else{
layUiUtil.setCode(0);
layUiUtil.setMsg("新增失败!");
}
return layUiUtil;
}
五、持久层不再赘述
六、前端
<!-- 上传excel表格 区域-->
<div class="layui-form-item" id="import" style="display:none;">
<form class="layui-form" onsubmit="return false;" id="readExcel">
<div class="layui-form-item">
<label class="layui-form-label"></label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn" id="test1">上传文件</button>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="demo">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<script type="text/javascript" src="../lib/layui-v2.6.3/layui.js"></script>
<script src="/static/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
layui.use(['table', 'layer', 'form', 'transfer','upload','element'], function () {
//常规使用 - excel表格上传
upload.render({
elem: '#test1'
,accept: 'file' //普通文件
,exts: 'xlsx' //上传文件格式
, url: 'http://localhost:8080/excel/read'
,data: {
stuCollegeNo: $userCollegeNo
} //可选项。额外的参数,如:{id: 123, abc: 'xxx'}
, done: function (res) {
console.log(res);
//如果上传失败
if (res.code > 0) {
layer.msg('上传失败');
} else {
active.reload();
layer.msg(res.msg, {
icon: 6,
time: 1500 //2秒关闭(如果不配置,默认是3秒)
}, function () {
//关闭弹出层
layer.closeAll();
element.progress('demo', '0%'); //进度条复位
});
}
}
//进度条
, progress: function (n, elem, e) {
element.progress('demo', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) {
//layer.msg('上传完毕', { icon: 1 });
}
}
});
});
</script>
总结
springboot环境下,和SSM下使用EasyExcel导入和导出最大的区别是,springboot环境下不需要配置配置上传组件,因为springboot是去配置化开发,相比之下,springboot比SSM要好用的多