SpringBoot从入门到精通教程(九)

前段时间公司开发用到了excel导入导出的功能,于是我就想到了easyExcel ,今天做一个简单的记录

【easyexcel简介】

阿里出品用来处理Excel的开源工具

为什么要使用 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模式。在上层做了模型转换的封装,让使用者更加简单方便

【GitHub官网地址】

https://github.com/alibaba/easyexcel

在这里插入图片描述

官网文档:

https://alibaba-easyexcel.github.io/

在这里插入图片描述

【推荐文章】

阿里 EasyExcel 7 行代码优雅地实现 Excel 文件生成&下载功能
https://segmentfault.com/a/1190000019472781#articleHeader13
【maven依赖】

https://mvnrepository.com/artifact/com.alibaba/easyexcel

在这里插入图片描述

在这里插入图片描述
所以本文demo使用2.1.6版本

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.6</version>
</dependency>

【项目】

【初始化sql脚本】

执行脚本,创建初始数据

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
  `id`       bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name`     varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名',
  `age`      int(3) DEFAULT NULL COMMENT '年龄',
  `birthday` date   DEFAULT NULL COMMENT '出生日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE =utf8mb4_unicode_ci;


INSERT INTO `user`
VALUES ('1', '张三', '23', '1996-10-23');
INSERT INTO `user`
VALUES ('2', '李四', '24', '1995-10-30');

在这里插入图片描述

【实体类 User】

package com.spiritmark.cloud.domain;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Data;

import java.util.Date;

@Data
public class User {
    // 主键id
    @ExcelIgnore // 生成报表时忽略,不生成次字段
    private Integer id;
    @ExcelProperty(value = "姓名", index = 0) // 定义表头名称和位置,0代表第一列
    private String name;
    @ExcelProperty(value = "年龄", index = 1)
    private Integer age;
    @ColumnWidth(20) // 定义列宽
    @DateTimeFormat(value = "yyyy/MM/dd")
    @ExcelProperty(value = "出生日期", index = 2)
    private Date birthday;
}

【工具类】

package com.spiritmark.cloud.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;

/**
 * Excel工具类
 */
@Slf4j
public class ExcelUtils {

    /**
     * 导出Excel(07版.xlsx)到指定路径下
     *
     * @param path      路径
     * @param excelName Excel名称
     * @param sheetName sheet页名称
     * @param clazz     Excel要转换的类型
     * @param data      要导出的数据
     */
    public static void export2File(String path, String excelName, String sheetName, Class clazz, List data) {
        String fileName = path.concat(excelName).concat(ExcelTypeEnum.XLSX.getValue());
        EasyExcel.write(fileName, clazz).sheet(sheetName).doWrite(data);
    }

    /**
     * 导出Excel(07版.xlsx)到web
     *
     * @param response  响应
     * @param excelName Excel名称
     * @param sheetName sheet页名称
     * @param clazz     Excel要转换的类型
     * @param data      要导出的数据
     * @throws Exception
     */
    public static void export2Web(HttpServletResponse response, String excelName, String sheetName, Class clazz, List data) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        excelName = URLEncoder.encode(excelName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());
        EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
    }

    /**
     * 将指定位置指定名称的Excel导出到web
     *
     * @param response  响应
     * @param path      文件路径
     * @param excelName 文件名称
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String export2Web4File(HttpServletResponse response, String path, String excelName) throws UnsupportedEncodingException {
        File file = new File(path.concat(excelName).concat(ExcelTypeEnum.XLSX.getValue()));
        if (!file.exists()) {
            return "文件不存在!";
        }

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        excelName = URLEncoder.encode(excelName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());

        try (
                FileInputStream in = new FileInputStream(file);
                ServletOutputStream out = response.getOutputStream();
        ) {
            IOUtils.copy(in, out);
            return "导出成功!";
        } catch (Exception e) {
            log.error("导出文件异常:", e);
        }

        return "导出失败!";
    }

}

【调用】

package com.spiritmark.cloud.controller;

import com.alibaba.excel.EasyExcel;
import com.spiritmark.cloud.annotation.LogAnnotation;
import com.spiritmark.cloud.domain.User;
import com.spiritmark.cloud.listener.UserDataListener;
import com.spiritmark.cloud.service.UserService;
import com.spiritmark.cloud.util.ExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@Slf4j
@Controller
@RequestMapping("/user")
public class UserController {

    // 在application.yml中配置
    @Value("${excel.path}")
    private String path;

    @Autowired
    private UserService userService;

    @RequestMapping("/toUserListPage")
    public String toUserListPage() {
        return "user/userList";
    }

    @LogAnnotation(description = "获取所有用户信息", clazz = User.class)
    @RequestMapping("/getAll")
    @ResponseBody
    public List<User> getAll(User user) {
        return userService.getAll();
    }

    // easyexcel导出Excel到指定位置
    @GetMapping("/export2File")
    @ResponseBody
    public String export2File() {
        ExcelUtils.export2File(path, "用户表", "用户信息", User.class, userService.getAll());
        return "导出成功";
    }

    // easyexcel导出Excel到web
    @GetMapping("/export2Web")
    public void export2Web(HttpServletResponse response) {
        try {
            ExcelUtils.export2Web(response, "用户表", "用户信息", User.class, userService.getAll());
        } catch (Exception e) {
            log.error("报表导出异常:", e);
        }
    }

    // 将指定位置指定名称的Excel导出到web
    @GetMapping("/export2Web4File/{excelName}")
    @ResponseBody
    public String export2Web4File(HttpServletResponse response, @PathVariable String excelName) {
        try {
            return ExcelUtils.export2Web4File(response, path, excelName);
        } catch (Exception e) {
            log.error("文件导出异常:", e);
        }

        return "文件导出失败";
    }

    // easyexcel读取文件
    @GetMapping("/read4File")
    @ResponseBody
    public String read4File() {
        String fileName = path + "用户表导入.xlsx";
        EasyExcel.read(fileName, User.class, new UserDataListener(userService)).sheet().doRead();
        return "读取成功";
    }

    // 跳转到上传页面
    @RequestMapping("/toUploadPage")
    public String toUploadPage() {
        return "user/upload";
    }

    // easyexcel上传文件
    @PostMapping("/upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), User.class, new UserDataListener(userService)).sheet().doRead();
        return "上传成功";
    }

}

【UserDataListener】

package com.spiritmark.cloud.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.spiritmark.cloud.domain.User;
import com.spiritmark.cloud.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

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

@Slf4j
public class UserDataListener extends AnalysisEventListener<User> {

    private UserService userService;

    public UserDataListener(UserService userService) {
        this.userService = userService;
    }

    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<User> list = new ArrayList<User>();

    @Override
    public void invoke(User data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
        log.info("所有数据解析完成!");
    }

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

【测试】

【1】导出Excel到指定位置

http://localhost:8080/user/export2File

在这里插入图片描述
在这里插入图片描述

【2】导出Excel到web

http://localhost:8080/user/export2Web

在这里插入图片描述

在这里插入图片描述【3】将指定位置指定名称的Excel导出到web

http://localhost:8080/user/export2Web4File/{excelName}

例: http://localhost:8080/user/export2Web4File/ 用户表导入

在这里插入图片描述
【4】easyexcel读取文件

http://localhost:8080/user/read4File

在这里插入图片描述

在这里插入图片描述处理逻辑是将Excel中的数据保存到数据库中

在这里插入图片描述

在这里插入图片描述导入前数据库数据
在这里插入图片描述选择要导入的文件

在这里插入图片描述在这里插入图片描述点击“提交”,把数据保存到数据库

在这里插入图片描述

在这里插入图片描述OK,大功告成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章