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相关依赖


        com.alibaba
        easyexcel
        2.1.1
    

先在Java内进行测试

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

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

@Data
public class Stu {
    //设置表头名称
    @ExcelProperty("学生编号")
    private int sno;
    //设置表头名称
    @ExcelProperty("学生姓名")
    private String sname;
}

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

package com.atguigu.excel;

import com.alibaba.excel.EasyExcel;

import java.util.ArrayList;
import java.util.List;

public class TestWrite {
    public static void main(String[] args) {
        //设置文件名称和路径
        String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
        EasyExcel.write(fileName, User.class)
                .sheet("写操作")
                .doWrite(data());
    }
    //循环设置要添加的数据,最终封装到list集合中
    private static List data() {
        List list = new ArrayList();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i+1);
            user.setName("张三");
            list.add(user);
        }
        return list;
    }
}

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第1张图片

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第2张图片

4.EasyExcel读取Excle表格数据

先实现一个EasyExcel提供的类AnalysisEventListener

package com.atguigu.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 java.util.Map;

public class ExcelListener extends AnalysisEventListener {
    //一行一行去读取excel内容并封装到User中
    //默认从第二行读取数据,因为第一行一般都是表头
    @Override
    public void invoke(User user, AnalysisContext context) {
        System.out.println(user.toString());
    }
    //读取表头内容
    @Override
    public void invokeHead(Map headMap, AnalysisContext context) {
        //System.out.println("表头:"+headMap);
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {

    }
}

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

package com.atguigu.excel;

import com.alibaba.excel.EasyExcel;

public class TestRead {
    public static void main(String[] args) {
        String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
        EasyExcel.read(fileName, User.class,new ExcelListener())
                .sheet()
                .doRead();
    }
}

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第3张图片

与前端页面进行集成

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

注:这里使用的是Mybatis-plus

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

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

package com.atguigu.ggkt.vo.vod;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * 

* Dict *

* * @author qy */ @Data public class SubjectEeVo { @ExcelProperty(value = "id" ,index = 0) private Long id; @ExcelProperty(value = "课程分类名称" ,index = 1) private String title; @ExcelProperty(value = "上级id" ,index = 2) private Long parentId; @ExcelProperty(value = "排序" ,index = 3) private Integer sort; }
package com.atguigu.ggkt.model.vod;

import com.atguigu.ggkt.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Data
@ApiModel(description = "Subject")
@TableName("subject")
public class Subject {

	private static final long serialVersionUID = 1L;
	@ApiModelProperty(value = "id")
	private Long id;

	@ApiModelProperty(value = "创建时间")
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@TableField("create_time")
	private Date createTime;

	@ApiModelProperty(value = "更新时间")
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@TableField("update_time")
	private Date updateTime;

	@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
	@JsonIgnore
	@TableLogic
	@TableField("is_deleted")
	private Integer isDeleted;

	@ApiModelProperty(value = "其他参数")
	@TableField(exist = false)
	private Map param = new HashMap<>();

	@ApiModelProperty(value = "类别名称")
	@TableField("title")
	private String title;

	@ApiModelProperty(value = "父ID")
	@TableField("parent_id")
	private Long parentId;

	@ApiModelProperty(value = "排序字段")
	@TableField("sort")
	private Integer sort;

	@ApiModelProperty(value = "是否包含子节点")
	@TableField(exist = false)
	private boolean hasChildren;

}

 2.Service接口与Impl

package com.atguigu.ggkt.vod.service;


import com.atguigu.ggkt.model.vod.Subject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;

/**
 * 

* 课程科目 服务类 *

* * @author atguigu * @since 2023-04-19 */ public interface SubjectService extends IService { void exportData(HttpServletResponse response); void importDictData(MultipartFile file); }

package com.atguigu.ggkt.vod.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.ggkt.model.vod.Subject;
import com.atguigu.ggkt.vo.vod.SubjectEeVo;
import com.atguigu.ggkt.vod.listener.SubjectListener;
import com.atguigu.ggkt.vod.mapper.SubjectMapper;
import com.atguigu.ggkt.vod.service.SubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * 

* 课程科目 服务实现类 *

* * @author atguigu * @since 2023-04-19 */ @Service public class SubjectServiceImpl extends ServiceImpl implements SubjectService { @Resource SubjectService subjectService; @Resource SubjectListener subjectListener; //课程分类导出功能 @Override public void exportData(HttpServletResponse response) { try{ //设置从数据库下载值的信息 response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和EasyExcel没有关系 String fileName = URLEncoder.encode("课程分类", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx"); List subjects = subjectService.list(); List subjectEeVos = new ArrayList<>(subjects.size()); for(Subject dict : subjects) { SubjectEeVo dictVo = new SubjectEeVo(); // dictVo.setId(dict.getId()); // dictVo.setParentId(dict.getParentId()); // 以上的赋值写法较为复杂,Java提供一个方法让一个对象的属性赋值给另一个对象 BeanUtils.copyProperties(dict,dictVo); subjectEeVos.add(dictVo); } EasyExcel.write(response.getOutputStream(), SubjectEeVo.class) .sheet("课程分类") .doWrite(subjectEeVos); }catch (Exception e){ e.printStackTrace(); } } @Override public void importDictData(MultipartFile file) { try { EasyExcel.read(file.getInputStream(),SubjectEeVo.class,subjectListener) .sheet().doRead(); } catch (IOException e) { throw new RuntimeException(e); } } }

 3.Controller

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

package com.atguigu.ggkt.vod.controller;


import com.atguigu.ggkt.model.vod.Subject;
import com.atguigu.ggkt.result.Result;
import com.atguigu.ggkt.vod.service.SubjectService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 

* 课程科目 前端控制器 *

* * @author atguigu * @since 2023-04-19 */ @RestController @RequestMapping("/admin/vod/subject") @CrossOrigin(origins = "*") public class SubjectController { @Resource SubjectService subjectService; @ApiOperation("课程分类导出") @GetMapping("/exportData") public void exportData(HttpServletResponse response){ subjectService.exportData(response); } @ApiOperation(value = "导入") @PostMapping("importData") public Result importData(MultipartFile file) { subjectService.importDictData(file); return Result.ok(null); } }

4.前端页面

导出按钮与对应的方法

导出
exportData() {
    window.open("http://localhost:8301/admin/vod/subject/exportData")
}

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

按钮

 导入

点击对应的弹出层


    
        
            
                点击上传
                
只能上传xls文件,且不超过500kb

data中添加弹出层的属性

data() {
    return {
        dialogImportVisible: false,
        list:[] //数据字典列表数组
    }
}

添加导入方法

importData() {
    this.dialogImportVisible = true
},
onUploadSuccess(response, file) {
    this.$message.info('上传成功')
    this.dialogImportVisible = false
    this.getSubList(0)
},

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

测试!!

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第4张图片

 EasyExcel使用教程-实现页面中批量导入导出数据-详解_第5张图片

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

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第6张图片

 EasyExcel使用教程-实现页面中批量导入导出数据-详解_第7张图片

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

EasyExcel使用教程-实现页面中批量导入导出数据-详解_第8张图片

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

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

你可能感兴趣的:(excel)