SpringBoot整合EasyExcel实现Excel导入导出/上传下载至数据库

上一篇写了一个SpringBoot整合poi实现excel导入导出的,但是使用poi会有一些问题,这时阿里带着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模式。在上层做了模型转换的封装,让使用者更加简单方便。

这个整合EasyExcel的demo是在上一篇整合poi的基础上改进的,这里给个传送门SpringBoot实现Excel文件上传至数据库及下载数据库数据为Excel
源码已上传至码云
下面贴出修改的地方
先改一下依赖pom.xml

<!-- easyexcel相关依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.1.2-beta5</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>

测试的页面index.html修改如下
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传excl</title>
    <script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js">
    </script>
</head>
<body>
<div class="panel panel-primary">
     <!-- .panel-heading 面板头信息。 -->
     <div class="panel-heading">
      <!-- .panel-title 面板标题。 -->
      <h1 class="panel-title">上传excl并插入到数据库</h1>
     </div>
</div>
<form class="form-horizontal" action="/user/uploadExcl" enctype="multipart/form-data" method="post">
    <div class="form-group">
        <div class="input-group col-md-4">
                    <span class="input-group-addon">
                <i class="glyphicon glyphicon-search"></i>
            </span>
                  <input class="form-control" placeholder="请选择文件" type="file" name="file"/>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-4">
            <div class="btn-group btn-group-justified" >
                <div class="btn-group" >
                    <button type="submit" class="btn btn-success" id="submitbtn">
                        <span class="glyphicon glyphicon-share"></span>&nbsp;文件上传poi</button>
                </div>
            </div>
        </div>
    </div>
</form>

<form class="form-horizontal" action="/user/uploadEasyExcl" enctype="multipart/form-data" method="post">
    <div class="form-group">
        <div class="input-group col-md-4">
                    <span class="input-group-addon">
                <i class="glyphicon glyphicon-search"></i>
            </span>
                  <input class="form-control" placeholder="请选择文件" type="file" name="file"/>
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-4">
            <div class="btn-group btn-group-justified" >
                <div class="btn-group" >
                    <button type="submit" class="btn btn-success" id="submitbtn">
                        <span class="glyphicon glyphicon-share"></span>&nbsp;文件上传EasyExcel</button>
                </div>
            </div>
        </div>
    </div>
</form>



<table border="0" style="margin-top:4px; margin-left: 18px">
    <tr>
        <td><a href="#" class="easyui-linkbutton" onclick="downloadfile1();">数据导出poi</a></td>

    </tr>
    <tr>
        <td><a href="#" class="easyui-linkbutton" onclick="downloadfile2();">数据导出easyexcel</a></td>

    </tr>
</table>

<script>
    function downloadfile1(){
        window.location.href="/user/UserExcelDownloads";
    }
    function downloadfile2(){
        window.location.href="/user/UserExcelDownloadsEasyExcel";
    }
</script>

</body>
</html>

导出
user实体类
导出 Excel 时,若需要表头,那么相应的实体类需要继承 BaseRowModel,并加入 @ExcelProperty(value = “id”, index = 0) 注解。其中 value 代表在导出 Excel 时,该字段对应的表头名称;index 代表该字段对应的表头位置(从0开始)

package com.thz.excl_upload.entity;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;

import java.io.Serializable;
import java.util.Date;

/**
 * (User)实体类
 *
 * @author makejava
 * @since 2020-07-22 09:57:53
 */
public class User extends BaseRowModel implements Serializable {
    private static final long serialVersionUID = -75075031034829113L;

    @ExcelProperty(value = {"ID"}, index = 0)
    private Integer id;

    @ExcelProperty(value = {"neme"}, index = 1)
    private String name;

    @ExcelProperty(value = {"性别"}, index = 2)
    private String sex;

    @ExcelProperty(value = {"创建时间"}, index = 3)
    private Date createTime;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

}

UserController中添加下面的方法

@RequestMapping("UserExcelDownloadsEasyExcel")
public void UserExcelDownloadsEasyExcel(HttpServletResponse response) throws IOException {
    ExcelWriter writer = EasyExcelFactory.getWriter(response.getOutputStream());
    // 写仅有一个 Sheet 的 Excel 文件, 此场景较为通用
    Sheet sheet1 = new Sheet(1, 0, User.class);

    // 第一个 sheet 名称
    sheet1.setSheetName("第一个sheet");

    // 写数据到 Writer 上下文中
    // 入参1: 数据库查询的数据list集合
    // 入参2: 要写入的目标 sheet
    writer.write(userService.queryAll(new User()), sheet1);

    // 将上下文中的最终 outputStream 写入到指定文件中
    response.setContentType("application/octet-stream");
    String fileName = "userinf"  + ".xls";
    response.setHeader("Content-disposition", "attachment;filename=" + fileName);
    response.flushBuffer();
    writer.finish();
}

导入
因为EasyExcel是依据实体类中的@ExcelProperty注解来解析Excel表中每一列的数据的,如果说上传表中数据和数据库中字段是对应的 ,那我们可以继续使用上面的实体类。但是通常业务传过来的表可能是只有一些关键字段,这时候我们还想进行上传需要修改原先User实体的注解与上传表一致,可这样导出的数据就不全了。为了解决这个问题需要再创建一个专门的上传UserUpLoad实体类。
在这里插入图片描述
我的表是上面这样的,所以新创建了如下UserUpLoad实体

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;

import java.io.Serializable;
import java.util.Date;

/**
 * (User)实体类
 *
 * @author makejava
 * @since 2020-07-22 09:57:53
 */
public class UserUpLoad extends BaseRowModel implements Serializable {
    private static final long serialVersionUID = -75075031034829113L;

    private Integer id;

    @ExcelProperty(value = {"neme"}, index = 0)
    private String name;

    @ExcelProperty(value = {"性别"}, index = 1)
    private String sex;

    private Date createTime;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

}

UserUpLoadDao

/**
 * (UserUpLoad)表数据库访问层
 *
 * @author makejava
 * @since 2020-07-27 09:57:53
 */
@Mapper
@Component
public interface UserUpLoadDao {

    /**
     * 新增数据
     *
     * @param user 实例对象
     * @return 影响行数
     */
    int insert(UserUpLoad user);

}

UserUpLoadDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thz.excl_upload.dao.UserUpLoadDao">

    <resultMap type="com.thz.excl_upload.entity.UserUpLoad" id="UserMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="sex" column="sex" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <!--新增所有列-->
    <insert id="insert" keyProperty="id" useGeneratedKeys="true">
        insert into excl_test.user(name, sex, create_time)
        values (#{name}, #{sex}, #{createTime})
    </insert>

</mapper>

实体类的准备工作做完了,下面修改一下Controller

@RequestMapping(value = "/uploadEasyExcl")
public @ResponseBody
    Map<String ,Object> uploadEasyExcl(HttpServletRequest request, @RequestParam("file") MultipartFile file) throws IOException {
    Map<String ,Object> result = new HashMap<>();
    userService.saveUser(file);
    return result;
}

UserServiceImpl

@Override
public void saveUser(MultipartFile file) throws IOException {
    if(!file.getOriginalFilename().equals("上传测试.xls") && !file.getOriginalFilename().equals("上传测试.xlsx") ){
        return;
    }
    InputStream inputStream = new BufferedInputStream(file.getInputStream());
    //实例化实现了AnalysisEventListener接口的类
    ExcelListener excelListener = new ExcelListener(userUpLoadDao);
    ExcelReader reader = new ExcelReader(inputStream,null,excelListener);
    //读取信息
    reader.read(new Sheet(1,1,UserUpLoad.class));
}

ExcelListener

package com.thz.excl_upload.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.thz.excl_upload.dao.UserUpLoadDao;
import com.thz.excl_upload.entity.UserUpLoad;

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

public class ExcelListener extends AnalysisEventListener<UserUpLoad> {
    private List<UserUpLoad> datas = new ArrayList<>();
    private static final int BATCH_COUNT = 3000;
    private UserUpLoadDao userUpLoadDao;

    public ExcelListener(UserUpLoadDao userUpLoadDao){
        this.userUpLoadDao = userUpLoadDao;
    }

    @Override
    public void invoke(UserUpLoad user, AnalysisContext analysisContext) {
        //数据存储到datas,供批量处理,或后续自己业务逻辑处理。
        datas.add(user);
        //达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if(datas.size() >= BATCH_COUNT){
            saveData();
            // 存储完成清理datas
            datas.clear();
        }
    }

    private void saveData() {
        for(UserUpLoad user : datas){
            user.setCreateTime(new Date());
            this.userUpLoadDao.insert(user);
        }
    }

    public List<UserUpLoad> getDatas() {
        return datas;
    }

    public void setDatas(List<UserUpLoad> datas) {
        this.datas = datas;
    }

    /**
     * 所有数据解析完成了 都会来调用
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveData();//确保所有数据都能入库
    }
}

参考Springboot整合easyExcel导入导出Excel

  • 9
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
你好!对于使用Spring BootEasyExcel实现Excel导入导出,你可以按照以下步骤进行操作: 1. 添加依赖:在你的Spring Boot项目的pom.xml文件中添加EasyExcel的依赖。 ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.11</version> </dependency> ``` 2. 创建实体类:创建一个实体类,用于映射Excel中的数据。 ```java public class User { private String name; private Integer age; // 省略 getter 和 setter 方法 } ``` 3. 导出Excel:使用EasyExcel提供的工具类进行导出操作。 ```java public void exportExcel(List<User> userList, HttpServletResponse response) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("用户列表", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), User.class).sheet("用户列表").doWrite(userList); } ``` 4. 导入Excel:使用EasyExcel提供的监听器类进行导入操作。 ```java public void importExcel(MultipartFile file) throws IOException { EasyExcel.read(file.getInputStream(), User.class, new AnalysisEventListener<User>() { @Override public void invoke(User user, AnalysisContext context) { // 处理每一行数据 } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 所有数据处理完成后的操作 } }).sheet().doRead(); } ``` 以上就是使用Spring BootEasyExcel实现Excel导入导出的基本步骤。你可以根据自己的需求对代码进行适当的调整和扩展。希望对你有所帮助!如果有任何问题,请随时提问。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值