EasyExcel 实现导入与导出功能(Springboot+Vue)

Easy Excel

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

EasyExcel底层对POI进行了封装,重写了poi的方法。在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析,遇到再大的excel都不会出现内存溢出的问题,能够将一个原本3M的excel文件,POI来操作将会占用内存100M,使用EasyExcel降低到几KB,并且使用起来更加简单。

数据导入导出执行原理和思路:
用户端逻辑:
1. 数据导入

        用户先下载模板,根据模板填入数据,然后点击上传;

2. 数据导出

        用户在界面选择需要导出的数据(导出条件),点击导出。

后台开发逻辑:
1. 模板下载

        利用easyExcel生成文件,然后将文件放进响应流中,同时设置响应头为文件下载,浏览器收到响应之后,回去解析流中的内容,然后进行下载。

2. 文件上传

        用在填写好Excel内容之后,会以文件上传的形式,将文件上传到服务端,此时,我们只需要利用EasyExcel将文件流中的数据读出来即可。

3. 数据导出

        后台在接收到用户的数据导出请求之后,会根据请求中的筛选条件,查询对应数据,再将对应的数据填充进对应的导出模板中,以流的形式响应给浏览器。其实和模板下载的差不错,只是模板下载没有数据,数据导出有数据而已。

导入easyexcel依赖

    
            com.alibaba
            easyexcel
            3.1.0
        

导出功能

实体类

 */
@TableName(value ="score")
@Data
@AllArgsConstructor
@NoArgsConstructor

public class Score implements Serializable {
    public Score(String name, String xuehao, String score) {
        this.name = name;
        this.xuehao = xuehao;
        this.score = score;
    }

    /**
     *
     */
    @ExcelProperty("ID")
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     *
     */
    @ExcelProperty("姓名")
    private String name;

    /**
     *
     */
    @ExcelProperty("学号")
    private String xuehao;

    /**
     *
     */
    @ExcelProperty("成绩")
    private String score;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

后端代码

@RequestMapping("/index/load")
    @ResponseBody
    public String load(HttpServletResponse response) throws Exception {
        List list = scoreMapper.selectList(null);
        if(list==null)
            return "error";
        String filename=System.currentTimeMillis()+"";
        ServletOutputStream servletOutputStream = servletOutputStream(filename, response);
        EasyExcel.write(servletOutputStream,Score.class).sheet("成绩信息").doWrite(list);
        servletOutputStream.flush();
        return "success";


    }

    public static ServletOutputStream  servletOutputStream(String filename,HttpServletResponse response) throws Exception {
        try {
            filename = URLEncoder.encode(filename, "UTF-8");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf8");
            response.setHeader("Content-Disposition", "attachment; filename=" + filename + ".xlsx");
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            return response.getOutputStream();
        } catch (IOException e) {
            throw new Exception("导出excel表格失败!", e);
        }
    }

前端vue代码

  
            
            导出


 const daochu=()=>{
               window.open('http://localhost:8080/index/load')
            }

导入功能

监听器

package com.example.demo.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.example.demo.mybatisx.entity.Score;
import com.example.demo.mybatisx.mapper.ScoreMapper;
import com.example.demo.mybatisx.service.ScoreService;
import com.example.demo.utils.DemoDAO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener {

    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */

    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;

    private ScoreMapper scoreMapper;
    private ScoreService  scoreService;

    public DemoDataListener(ScoreService scoreService) {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
       this.scoreService=scoreService;
    }

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param score    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(Score score, AnalysisContext context) {
       score.setId(null);
       System.out.println(score);
        cachedDataList.add(score);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        scoreService.saveBatch(cachedDataList);
        log.info("存储数据库成功!");
    }


}

后端代码 MultipartFile是spring里定义的接口,它封装了用户在上传文件时所包含的所有信息。


    @RequestMapping("/index/import")
    @ResponseBody
    public String imp( @RequestParam("file") MultipartFile multipartFile) throws IOException {
        InputStream inputStream = multipartFile.getInputStream();
        EasyExcel.read(inputStream,Score.class,new DemoDataListener(scoreService)).sheet().doRead();
        return "success";

    }

Vue 前端代码

 
            
                
                导入
        




const importsuccess=(response,file)=>{
                console.log(response,file)
                alert("导入成功")
                load()
            }

你可能感兴趣的:(Springboot,Vue,vue.js,spring,boot,java)