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,大功告成

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