一.Excel导入导出的应用场景
1.数据导入:减轻录入的工作量
2.数据导出:统计信息归档
3.数据传输:异构系统之间数据传输
二。EasyExcel简介
1.EasyExcel特点
Java领域解析,生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc.
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
1.导入maven依赖,主要还需要poi的依赖,并且版本要对应上
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
<!--xls-->
<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>
2.编写entity对象类
@Data public class DemoData { //设置excel表头名称 @ExcelProperty(value = "学生编号",index = 0) private Integer sno; @ExcelProperty(value = "学生姓名",index = 1) private String sname; }
3.写操作
public class TestEasyExcelWrite { public static void main(String[] args) { //实现excel写的操作 //1 设置写入文件夹地址和excel文件名称 String filename = "C:\Users\aus\write.xlsx"; // 2 调用easyexcel里面的方法实现写操作 // write方法两个参数:第一个参数文件路径名称,第二个参数实体类class EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData()); } //创建方法返回list集合 private static List<DemoData> getData() { List<DemoData> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { DemoData data = new DemoData(); data.setSno(i); data.setSname("lucy"+i); list.add(data); } return list; } }
4.最终效果
5.读取excel需要先写一个监听器继承AnalysisEventListener
public class ExcelListener extends AnalysisEventListener<DemoData> { //一行一行读取excel内容 @Override public void invoke(DemoData data, AnalysisContext analysisContext) { System.out.println("****"+data); } //读取表头内容 @Override public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) { System.out.println("表头:"+headMap); } //读取完成之后 @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }
6.读取excel代码
public class TestEasyExcelRead { public static void main(String[] args) { //实现excel读操作 String filename = "C:\Users\aus\write.xlsx"; EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead(); } }
打印效果见下图:
实际项目中开发
entity
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @ApiModel(value="EduSubject对象", description="课程科目") public class EduSubject implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "课程类别ID") @TableId(value = "id", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = "类别名称") private String title; @ApiModelProperty(value = "父ID") private String parentId; @ApiModelProperty(value = "排序字段") private Integer sort; @ApiModelProperty(value = "创建时间") @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = "更新时间") @TableField(fill = FieldFill.INSERT) private Date gmtModified; }
@Data public class SubjectData { @ExcelProperty(index = 0) private String oneSubjectName; @ExcelProperty(index = 1) private String twoSubjectName; }
Controller层
@RestController @RequestMapping("/eduservice/subject") @CrossOrigin public class EduSubjectController { @Autowired private EduSubjectService subjectService; //添加课程分类 //获取上传过来文件,把文件内容读取出来 @PostMapping("addSubject") public R addSubject(MultipartFile file) { //上传过来excel文件 subjectService.saveSubject(file,subjectService); return R.ok(); } }
service层
@Service public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService { @Override public void saveSubject(MultipartFile file, EduSubjectService subjectService) { try{ InputStream in = file.getInputStream(); EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead(); } catch (Exception e) { e.printStackTrace(); } } }
5.监听器继承AnalysisEventListener,这里需要注意的点,因为业务层的SubjectExcelListener 并没有交给Spring容器来管理,所以监听器代码无法注入EduSubjectService ,
这里通过构造方法的方式传过来,从而完成对数据库的操作。
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> { //因为SubjectExcelListener不交给spring进行管理,需要自己new,不能注入其他对象 //不能实现数据库操作 public EduSubjectService subjectService; public SubjectExcelListener() {} public SubjectExcelListener(EduSubjectService subjectService) { this.subjectService = subjectService; } //读取excel内容,一行一行进行读取 @Override public void invoke(SubjectData subjectData, AnalysisContext analysisContext) { if(subjectData == null) { throw new GuliException(20001,"文件数据为空"); } //一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类 //判断一级分类是否重复 EduSubject existOneSubject = this.existOneSubject(subjectService, subjectData.getOneSubjectName()); if(existOneSubject == null) { //没有相同一级分类,进行添加 existOneSubject = new EduSubject(); existOneSubject.setParentId("0"); existOneSubject.setTitle(subjectData.getOneSubjectName());//一级分类名称 subjectService.save(existOneSubject); } //获取一级分类id值 String pid = existOneSubject.getId(); //添加二级分类 //判断二级分类是否重复 EduSubject existTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid); if(existTwoSubject == null) { existTwoSubject = new EduSubject(); existTwoSubject.setParentId(pid); existTwoSubject.setTitle(subjectData.getTwoSubjectName());//二级分类名称 subjectService.save(existTwoSubject); } } //判断一级分类不能重复添加 private EduSubject existOneSubject(EduSubjectService subjectService,String name) { QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title",name); wrapper.eq("parent_id","0"); EduSubject oneSubject = subjectService.getOne(wrapper); return oneSubject; } //判断二级分类不能重复添加 private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid) { QueryWrapper<EduSubject> wrapper = new QueryWrapper<>(); wrapper.eq("title",name); wrapper.eq("parent_id",pid); EduSubject twoSubject = subjectService.getOne(wrapper); return twoSubject; } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } }