JAVA解析Excel工具EasyExcel
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便.
1.jar包
<!-- Excel模板导出依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.实体
@Data
//@ContentRowHeight(50) // 设置 Cell 高度 为50
//@HeadRowHeight(40) // 设置表头 高度 为 40
public class Student {
/**
* value: 表头名称
* index: 列的号, 0表示第一列
*/
//@ColumnWidth(40) // 设置 Cell 宽度
@ExcelProperty(value = "学生姓名",index = 0)
private String name;
@ExcelProperty(value = "学生年龄",index = 1)
private String age;
@ExcelProperty(value = "性别",index = 2)
private String nameGender;
@ExcelProperty(value = "修改时间",index = 4)
private Date updateTime;
@ExcelIgnore //忽略这个字段
private List<SubjectPersonnel> subjectPersonnels;
}
3.导出代码实现(controller )
@GetMapping("exportExcel")
public void exportExcel(HttpServletResponse response) throws Exception {
//1.获取要导出的数据
List<Student> data =studentService.queryDataAll();
//下载到浏览器默认地址
ServletOutputStream out = response.getOutputStream();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = simpleDateFormat.format(new Date());
//导出文件的名字
String fileName=new String((("学生")+date).getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1)+".xlsx";
//添加响应头信息,swagger会有乱码,正常的web不会有
response.setHeader("Content-disposition", "attachment; filename=" + fileName);
response.setContentType("application/msexcel;charset=UTF-8");//设置类型
response.setHeader("Pragma", "No-cache");//设置头
response.setHeader("Cache-Control", "no-cache");//设置头
response.setDateHeader("Expires", 0);//设置日期头
//第一个参数是路径,第二个是参数实体类的class
EasyExcel.write(out, Student.class).sheet("学生").doWrite(data);
}
4.读取excel(实现监听器)
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.util.ConverterUtils;
import com.sn.execel.dao.StudentDao;
import com.sn.execel.entity.Student;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @program: execel
* @description: 监听器类中处理读取的数据
* @author: shenning
* @create: 2020-08-20 10:34
*/
//有个很重要的点 ExcelListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class ExcelListener extends AnalysisEventListener<Student> {
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
/**
* 这个集合用于接收 读取Excel文件得到的数据
*/
private List<Student> studentList = new ArrayList<Student>();
private StudentDao studentDao;
public ExcelListener() { }
/**
*
* 不要使用自动装配
* 在测试类中将dao当参数传进来
*/
public ExcelListener(StudentDao studentDao) {
this.studentDao = studentDao;
}
/**
* 加上存储数据库
*/
private void saveData() {
//在这个地方可以调用dao
for(int i=0,j=studentList.size();i<j;i++){
studentDao.insertOne(studentList.get(i));
}
}
/**
* 一行一行的读取(Excel第一行不会读取)
* @param data
* @param context
*/
@Override
public void invoke(Student data, AnalysisContext context) {
//数据存储到list,供批量处理,或后续自己业务逻辑处理。
studentList.add(data);
if(studentList.size()>=BATCH_COUNT){
saveData();
//存储完成清理 list
studentList.clear();
}
}
/**
* 读取完成之后做的事情,都会来调用
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//这里也要保存数据,确保最后遗留的数据也存储到数据库
if(studentList.size()>0){
saveData();
//存储完成清理 list
studentList.clear();
}
}
/**
* 读取Excel 表头
* @param headMap
* @param context
*/
@Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
}
}
5.controller
//我这就是dao层,应该会service层
@Autowired
private StudentDao studentDao;
/**
* 导入
* @param file
* @throws Exception
*/
@PostMapping("readExcel")
public void readExcel(MultipartFile file) throws Exception {
//方法上传文件
InputStream inputStream = file.getInputStream();
//实例化实现了AnalysisEventListener接口的类
ExcelListener excelListener = new ExcelListener(studentDao);
/**
* 参数1 要读取的文件
* 参数2 要读取的数据对应的实体类类对象
* 参数3 监听器对象 可以在创建的时候把dao当做参数传进去
*/
EasyExcel.read(inputStream,Student.class,excelListener).sheet().doRead();
}