当前位置:   article > 正文

EasyExcel使用教程-实现页面中批量导入导出数据-详解_easyexcel导入数据

easyexcel导入数据

EasyExcel介绍

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasyExcel特点

  • Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

  • EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)

  • EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。

开始使用:

1.pom中引入xml相关依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>easyexcel</artifactId>
  4. <version>2.1.1</version>
  5. </dependency>

先在Java内进行测试

2.创建实体-设置表头和添加的数据字段

这里的ExcelProperty表示在Excle表格中第一行,表头的列名称

  1. @Data
  2. public class Stu {
  3. //设置表头名称
  4. @ExcelProperty("学生编号")
  5. private int sno;
  6. //设置表头名称
  7. @ExcelProperty("学生姓名")
  8. private String sname;
  9. }

3.写入用EasyExcel写入表格数据Excle

  1. package com.atguigu.excel;
  2. import com.alibaba.excel.EasyExcel;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. public class TestWrite {
  6. public static void main(String[] args) {
  7. //设置文件名称和路径
  8. String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
  9. EasyExcel.write(fileName, User.class)
  10. .sheet("写操作")
  11. .doWrite(data());
  12. }
  13. //循环设置要添加的数据,最终封装到list集合中
  14. private static List<User> data() {
  15. List<User> list = new ArrayList<User>();
  16. for (int i = 0; i < 10; i++) {
  17. User user = new User();
  18. user.setId(i+1);
  19. user.setName("张三");
  20. list.add(user);
  21. }
  22. return list;
  23. }
  24. }

4.EasyExcel读取Excle表格数据

先实现一个EasyExcel提供的类AnalysisEventListener

  1. package com.atguigu.excel;
  2. import com.alibaba.excel.context.AnalysisContext;
  3. import com.alibaba.excel.event.AnalysisEventListener;
  4. import com.alibaba.excel.metadata.CellData;
  5. import com.alibaba.excel.util.ConverterUtils;
  6. import java.util.Map;
  7. public class ExcelListener extends AnalysisEventListener<User> {
  8. //一行一行去读取excel内容并封装到User中
  9. //默认从第二行读取数据,因为第一行一般都是表头
  10. @Override
  11. public void invoke(User user, AnalysisContext context) {
  12. System.out.println(user.toString());
  13. }
  14. //读取表头内容
  15. @Override
  16. public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
  17. //System.out.println("表头:"+headMap);
  18. }
  19. @Override
  20. public void doAfterAllAnalysed(AnalysisContext context) {
  21. }
  22. }

5.编写读取测试类并执行

  1. package com.atguigu.excel;
  2. import com.alibaba.excel.EasyExcel;
  3. public class TestRead {
  4. public static void main(String[] args) {
  5. String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
  6. EasyExcel.read(fileName, User.class,new ExcelListener())
  7. .sheet()
  8. .doRead();
  9. }
  10. }

与前端页面进行集成

1.编写业务逻辑层与控制器

注:这里使用的是Mybatis-plus

先写两个实体类,第一个是数据库对应的实体类(EasyExcel中要求实体类属性个数与表中表头列的名称个数一致,我们一般实体类还会自己添加一些其他属性,比如说多表联查还需要嵌入一个属性是另一个表的实体类,那么这就不符合EasyExcel的规范,所以要写一个实体类属性与表格完全一致的实体类),第二个是你业务中需要的实体类(可能添加了一些其他的属性值,一般我们开发中都是用的这个,不需要添加)

这里第一个是我重新写的实体类,第二个是原本的实体类,被我自己加了属性,大家可以进行对比

  1. package com.atguigu.ggkt.vo.vod;
  2. import com.alibaba.excel.annotation.ExcelProperty;
  3. import lombok.Data;
  4. /**
  5. * <p>
  6. * Dict
  7. * </p>
  8. *
  9. * @author qy
  10. */
  11. @Data
  12. public class SubjectEeVo {
  13. @ExcelProperty(value = "id" ,index = 0)
  14. private Long id;
  15. @ExcelProperty(value = "课程分类名称" ,index = 1)
  16. private String title;
  17. @ExcelProperty(value = "上级id" ,index = 2)
  18. private Long parentId;
  19. @ExcelProperty(value = "排序" ,index = 3)
  20. private Integer sort;
  21. }
  1. package com.atguigu.ggkt.model.vod;
  2. import com.atguigu.ggkt.model.base.BaseEntity;
  3. import com.baomidou.mybatisplus.annotation.*;
  4. import com.fasterxml.jackson.annotation.JsonFormat;
  5. import com.fasterxml.jackson.annotation.JsonIgnore;
  6. import io.swagger.annotations.ApiModel;
  7. import io.swagger.annotations.ApiModelProperty;
  8. import lombok.Data;
  9. import java.util.Date;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. @Data
  13. @ApiModel(description = "Subject")
  14. @TableName("subject")
  15. public class Subject {
  16. private static final long serialVersionUID = 1L;
  17. @ApiModelProperty(value = "id")
  18. private Long id;
  19. @ApiModelProperty(value = "创建时间")
  20. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  21. @TableField("create_time")
  22. private Date createTime;
  23. @ApiModelProperty(value = "更新时间")
  24. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  25. @TableField("update_time")
  26. private Date updateTime;
  27. @ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
  28. @JsonIgnore
  29. @TableLogic
  30. @TableField("is_deleted")
  31. private Integer isDeleted;
  32. @ApiModelProperty(value = "其他参数")
  33. @TableField(exist = false)
  34. private Map<String,Object> param = new HashMap<>();
  35. @ApiModelProperty(value = "类别名称")
  36. @TableField("title")
  37. private String title;
  38. @ApiModelProperty(value = "父ID")
  39. @TableField("parent_id")
  40. private Long parentId;
  41. @ApiModelProperty(value = "排序字段")
  42. @TableField("sort")
  43. private Integer sort;
  44. @ApiModelProperty(value = "是否包含子节点")
  45. @TableField(exist = false)
  46. private boolean hasChildren;
  47. }

 2.Service接口与Impl

  1. package com.atguigu.ggkt.vod.service;
  2. import com.atguigu.ggkt.model.vod.Subject;
  3. import com.baomidou.mybatisplus.extension.service.IService;
  4. import org.springframework.web.multipart.MultipartFile;
  5. import javax.servlet.http.HttpServletResponse;
  6. /**
  7. * <p>
  8. * 课程科目 服务类
  9. * </p>
  10. *
  11. * @author atguigu
  12. * @since 2023-04-19
  13. */
  14. public interface SubjectService extends IService<Subject> {
  15. void exportData(HttpServletResponse response);
  16. void importDictData(MultipartFile file);
  17. }

  1. package com.atguigu.ggkt.vod.service.impl;
  2. import com.alibaba.excel.EasyExcel;
  3. import com.atguigu.ggkt.model.vod.Subject;
  4. import com.atguigu.ggkt.vo.vod.SubjectEeVo;
  5. import com.atguigu.ggkt.vod.listener.SubjectListener;
  6. import com.atguigu.ggkt.vod.mapper.SubjectMapper;
  7. import com.atguigu.ggkt.vod.service.SubjectService;
  8. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  9. import org.springframework.beans.BeanUtils;
  10. import org.springframework.stereotype.Service;
  11. import org.springframework.web.multipart.MultipartFile;
  12. import javax.annotation.Resource;
  13. import javax.servlet.http.HttpServletResponse;
  14. import java.io.IOException;
  15. import java.net.URLEncoder;
  16. import java.util.ArrayList;
  17. import java.util.List;
  18. /**
  19. * <p>
  20. * 课程科目 服务实现类
  21. * </p>
  22. *
  23. * @author atguigu
  24. * @since 2023-04-19
  25. */
  26. @Service
  27. public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {
  28. @Resource
  29. SubjectService subjectService;
  30. @Resource
  31. SubjectListener subjectListener;
  32. //课程分类导出功能
  33. @Override
  34. public void exportData(HttpServletResponse response) {
  35. try{
  36. //设置从数据库下载值的信息
  37. response.setContentType("application/vnd.ms-excel");
  38. response.setCharacterEncoding("utf-8");
  39. // 这里URLEncoder.encode可以防止中文乱码 当然和EasyExcel没有关系
  40. String fileName = URLEncoder.encode("课程分类", "UTF-8");
  41. response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
  42. List<Subject> subjects = subjectService.list();
  43. List<SubjectEeVo> subjectEeVos = new ArrayList<>(subjects.size());
  44. for(Subject dict : subjects) {
  45. SubjectEeVo dictVo = new SubjectEeVo();
  46. // dictVo.setId(dict.getId());
  47. // dictVo.setParentId(dict.getParentId());
  48. // 以上的赋值写法较为复杂,Java提供一个方法让一个对象的属性赋值给另一个对象
  49. BeanUtils.copyProperties(dict,dictVo);
  50. subjectEeVos.add(dictVo);
  51. }
  52. EasyExcel.write(response.getOutputStream(), SubjectEeVo.class)
  53. .sheet("课程分类")
  54. .doWrite(subjectEeVos);
  55. }catch (Exception e){
  56. e.printStackTrace();
  57. }
  58. }
  59. @Override
  60. public void importDictData(MultipartFile file) {
  61. try {
  62. EasyExcel.read(file.getInputStream(),SubjectEeVo.class,subjectListener)
  63. .sheet().doRead();
  64. } catch (IOException e) {
  65. throw new RuntimeException(e);
  66. }
  67. }
  68. }

 3.Controller

没有使用swagger的记得把@ApiOperation注解删掉

  1. package com.atguigu.ggkt.vod.controller;
  2. import com.atguigu.ggkt.model.vod.Subject;
  3. import com.atguigu.ggkt.result.Result;
  4. import com.atguigu.ggkt.vod.service.SubjectService;
  5. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  6. import io.swagger.annotations.ApiOperation;
  7. import org.springframework.web.bind.annotation.*;
  8. import org.springframework.web.multipart.MultipartFile;
  9. import javax.annotation.Resource;
  10. import javax.servlet.http.HttpServletResponse;
  11. import java.util.List;
  12. /**
  13. * <p>
  14. * 课程科目 前端控制器
  15. * </p>
  16. *
  17. * @author atguigu
  18. * @since 2023-04-19
  19. */
  20. @RestController
  21. @RequestMapping("/admin/vod/subject")
  22. @CrossOrigin(origins = "*")
  23. public class SubjectController {
  24. @Resource
  25. SubjectService subjectService;
  26. @ApiOperation("课程分类导出")
  27. @GetMapping("/exportData")
  28. public void exportData(HttpServletResponse response){
  29. subjectService.exportData(response);
  30. }
  31. @ApiOperation(value = "导入")
  32. @PostMapping("importData")
  33. public Result importData(MultipartFile file) {
  34. subjectService.importDictData(file);
  35. return Result.ok(null);
  36. }
  37. }

4.前端页面

导出按钮与对应的方法

  1. <div class="el-toolbar">
  2. <div class="el-toolbar-body" style="justify-content: flex-start;">
  3. <el-button type="text" @click="exportData"><i class="fa fa-plus"/> 导出</el-button>
  4. </div>
  5. </div>
  1. exportData() {
  2. window.open("http://localhost:8301/admin/vod/subject/exportData")
  3. }

导入按钮、点击按钮弹出的界面、弹出层的data值与对应的导入方法

按钮

<el-button type="text" @click="importData"><i class="fa fa-plus"/> 导入</el-button>

点击对应的弹出层

  1. <el-dialog title="导入" :visible.sync="dialogImportVisible" width="480px">
  2. <el-form label-position="right" label-width="170px">
  3. <el-form-item label="文件">
  4. <el-upload
  5. :multiple="false"
  6. :on-success="onUploadSuccess"
  7. :action="'http://localhost:8333/admin/vod/subject/importData'"
  8. class="upload-demo">
  9. <el-button size="small" type="primary">点击上传</el-button>
  10. <div slot="tip" class="el-upload__tip">只能上传xls文件,且不超过500kb</div>
  11. </el-upload>
  12. </el-form-item>
  13. </el-form>
  14. <div slot="footer" class="dialog-footer">
  15. <el-button @click="dialogImportVisible = false">取消</el-button>
  16. </div>
  17. </el-dialog>

data中添加弹出层的属性

  1. data() {
  2. return {
  3. dialogImportVisible: false,
  4. list:[] //数据字典列表数组
  5. }
  6. }

添加导入方法

  1. importData() {
  2. this.dialogImportVisible = true
  3. },
  4. onUploadSuccess(response, file) {
  5. this.$message.info('上传成功')
  6. this.dialogImportVisible = false
  7. this.getSubList(0)
  8. },

这里的this.getSubList(0)只是导入数据后调用重新加载页面数据的一个方法,可以自行编写

测试!!

 

准备好我们要导入到数据库的表格

 

然后打开树形菜单,可以发现表格中的数据已经被渲染上去了

对service中的sersheet方法和doWrite、doRead方法有疑问的这里解答一下,sersheet是excel表格中底端的那个名称,doRead通过监听器每次读取一行,这也是EasyExcel的优点,一次一行,读完这行再下一行不会全部读取出来,举个极端例子。比如说你内存仅剩100kb了,你表格有500kb,那是不是一下子就蹦了?那读一行完加载到内存中,再下一行就可以很好的解决了这种问题,doWrite就是写入的操作了

以上就是 EasyExcel的读写操作并且与Element-ui进行整合实现导入导出功能

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/程序安全守护者/article/detail/61050
推荐阅读
  

闽ICP备14008679号