首頁 > 軟體

SpringBoot整合EasyExcel實現Excel表格匯出功能

2022-07-28 14:02:03

栗子

在後端管理系統的開發中,經常有需要匯出當前表格資料的功能,有些前端表格元件可以直接做到,但是不夠靈活。因為前端拿到的資料始終是經過處理的,如果想拿到原版資料,必須後端處理。如下圖:

除了使用Apache POI包,還有沒有其他的選擇?當然有! 這裡我給大家推薦一款非常簡單且容易上手的開源元件:Alibaba EasyExcel

1.元件介紹

首先放出官網地址,歡迎大家star(目前已經24K): https://alibaba-easyexcel.github.io/docs/current/

EasyExcel是一個基於Java的簡單、省記憶體的讀寫Excel的開源專案。在儘可能節約記憶體的情況下支援讀寫百M的Excel。

64M記憶體20秒讀取75M(46W行25列)的Excel(3.0.2+版本)

 Alibaba EasyExcel的核心類是EasyExcel

  /**
     * 最簡單的讀
     * <p>1. 建立excel對應的實體物件 參照{@link DemoData}
     * <p>2. 由於預設一行行的讀取excel,所以需要建立excel一行一行的回撥監聽器,參照{@link DemoDataListener}
     * <p>3. 直接讀即可
     */
    @Test
    public void simpleRead() {
        String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
        // 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    }
/**
     * 最簡單的寫
     * <p>1. 建立excel對應的實體物件 參照{@link com.alibaba.easyexcel.test.demo.write.DemoData}
     * <p>2. 直接寫即可
     */
    @Test
    public void simpleWrite() {
        String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx";
        // 這裡 需要指定寫用哪個class去讀,然後寫到第一個sheet,名字為模板 然後檔案流會自動關閉
        // 如果這裡想使用03 則 傳入excelType引數即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
    }

2.組態檔

SpringBoot專案pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>SpringBoot-easyexcel</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringBoot-easyexcel</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 一般easyexcel都會和lombok搭配使用 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <!-- 最新版 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.專案程式碼

專案結構

ExportController.java

package com.example.springbooteasyexcel.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.example.springbooteasyexcel.data.Mock;
import com.example.springbooteasyexcel.sheet.CitySheet;
import com.example.springbooteasyexcel.sheet.CompanySheet;
import com.example.springbooteasyexcel.sheet.UserSheet;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
@RestController
@RequestMapping("/export")
public class ExportController {
    /**
     * @param response
     * @url <a>http://localhost:8080/export/test1</a>
     * 在Excel中寫入單個sheet
     */
    @RequestMapping("/test1")
    public void test1(HttpServletResponse response) {
        //從HttpServletResponse中獲取OutputStream輸出流
        try {
            // 設定響應型別
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            // 設定編碼格式
            response.setCharacterEncoding("utf-8");
            // 設定URLEncoder.encode 防止中文亂碼
            String fileName = URLEncoder.encode("使用者資訊表", "UTF-8").replaceAll("\+", "%20");
            // 設定響應頭
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 寫出Excel
            EasyExcel.write(response.getOutputStream(), UserSheet.class).inMemory(true).sheet("使用者資訊表").doWrite(Mock.userList());
        } catch (IOException e) {
            throw new RuntimeException("資料或檔案損壞,無法下載");
        }
    }

    /**
     * 在Excel中寫入多個sheet
     *
     * @url <a>http://localhost:8080/export/test2</a>
     */
    @RequestMapping("/test2")
    public void test2(HttpServletResponse response) throws Exception {
        // 設定響應型別
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        // 設定編碼格式
        response.setCharacterEncoding("utf-8");
        // 設定URLEncoder.encode 防止中文亂碼
        String fileName = URLEncoder.encode("資訊表", "UTF-8").replaceAll("\+", "%20");
        // 設定響應頭
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        // 多個sheet的輸出需要使用ExcelWriter類,這裡想要下載成功,需要輸出到OutputStream中
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).inMemory(true).build()) {
            // 建立使用者資訊表的sheet,寫入使用者資訊資料,1代表sheet的位置是第一個
            WriteSheet userInfoSheet = EasyExcel.writerSheet(0, "使用者資訊表").head(UserSheet.class).build();
            excelWriter.write(Mock.userList(), userInfoSheet);
            // 建立城市資訊表的sheet,寫入城市資訊資料,2代表sheet的位置是第二個
            WriteSheet cityInfoSheet = EasyExcel.writerSheet(1, "城市資訊表").head(CitySheet.class).build();
            excelWriter.write(Mock.cityList(), cityInfoSheet);
            // 建立公司資訊表的sheet,寫入公司資訊資料,3代表sheet的位置是第三個
            WriteSheet companyInfoSheet = EasyExcel.writerSheet(2, "公司資訊表").head(CompanySheet.class).build();
            excelWriter.write(Mock.companyList(), companyInfoSheet);
        }
    }
}

Mock.java

以下資料均來自於網路,如有侵權,請聯絡刪除

package com.example.springbooteasyexcel.data;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.RichTextStringData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.example.springbooteasyexcel.sheet.CitySheet;
import com.example.springbooteasyexcel.sheet.CompanySheet;
import com.example.springbooteasyexcel.sheet.UserSheet;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class Mock {
    public static List<UserSheet> userList() {
        List<UserSheet> list = new ArrayList<>(10);
        list.add(UserSheet.builder().userId(001L).userName("張三").userPhone("11112223123").userEmail("zhansan@163.com").userAddress("北京朝陽區").gender(buildCellData("男")).registerTime(Calendar.getInstance().getTime()).build());
        list.add(UserSheet.builder().userId(002L).userName("李四").userPhone("11112223123").userEmail("lisi@qq.com").userAddress("南京玄武門").gender(buildCellData("女")).registerTime(Calendar.getInstance().getTime()).build());
        list.add(UserSheet.builder().userId(003L).userName("王五").userPhone("11112223123").userEmail("wangwu@google.com").userAddress("杭州未來科技城").gender(buildCellData("男")).registerTime(Calendar.getInstance().getTime()).build());
        list.add(UserSheet.builder().userId(004L).userName("趙六").userPhone("11112223123").userEmail("zhaoliu@baidu.com").userAddress("上海徐家彙").gender(buildCellData("女")).registerTime(Calendar.getInstance().getTime()).build());
        return list;
    }
    private static WriteCellData<String> buildCellData(String gender) {
        // 設定單個單元格多種樣式
        WriteCellData<String> cellData = new WriteCellData<>();
       	// 設定單個單元格的填充型別
        cellData.setType(CellDataTypeEnum.RICH_TEXT_STRING);
        RichTextStringData richTextStringData = new RichTextStringData();
        cellData.setRichTextStringDataValue(richTextStringData);
        richTextStringData.setTextString(gender);
        WriteFont writeFont = new WriteFont();
        if ("男".equalsIgnoreCase(gender)) {
        	//設定顏色為紅色
            writeFont.setColor(IndexedColors.RED.getIndex());
        } else if ("女".equalsIgnoreCase(gender)) {
        	//設定顏色為綠色
            writeFont.setColor(IndexedColors.GREEN.getIndex());
        }
        //應用顏色字型
        richTextStringData.applyFont(writeFont);
        return cellData;
    }
    public static List<CitySheet> cityList() {
        List<CitySheet> list = new ArrayList<>(10);
        list.add(CitySheet.builder().cityName("杭州市").cityDesc("杭州市一般指杭州。 杭州,簡稱「杭」,古稱臨安、錢塘,浙江省轄地級市、省會、副省級市、特大城市、國務院批覆確定的浙江省經濟、文化、科教中心,長江三角洲中心城市之一,環杭州灣大灣區核心城市、G60科創走廊中心城市。").build());
        list.add(CitySheet.builder().cityName("合肥市").cityDesc("合肥市一般指合肥。 合肥,簡稱「廬」或「合」,古稱廬州、廬陽、合淝,安徽省轄地級市、省會,是合肥都市圈中心城市,國務院批覆確定的中國長三角城市群副中心城市,全國四大科教基地、現代製造業基地和綜合交通樞紐。").build());
        list.add(CitySheet.builder().cityName("武漢市").cityDesc("武漢市一般指武漢。 武漢,簡稱「漢」,別稱江城,是湖北省省會,中部六省唯一的副省級市,超大城市,中國中部地區的中心城市,全國重要的工業基地、科教基地和綜合交通樞紐,聯勤保障部隊機關駐地。").build());
        list.add(CitySheet.builder().cityName("深圳市").cityDesc("深圳市一般指深圳。 深圳,簡稱「深」,別稱鵬城,廣東省轄地級市,是廣東省副省級市,國家計劃單列市,超大城市,國務院批覆確定的中國經濟特區、全國性經濟中心城市、國際化城市、科技創新中心、區域金融中心、商貿物流中心。").build());
        return list;
    }
    public static List<CompanySheet> companyList() {
        List<CompanySheet> list = new ArrayList<>(10);
        list.add(CompanySheet.builder().companyName("阿里巴巴").companyBoss("馬雲").companyBase("杭州市").companyDesc("阿里巴巴集團經營多項業務,另外也從關聯公司的業務和服務中取得經營商業生態系統上的支援。業務和關聯公司的業務包括:淘寶網、天貓、聚划算、全球速賣通、阿里巴巴國際交易市場、1688、阿里媽媽、阿里雲、螞蟻集團 [408]  、菜鳥網路等。").build());
        list.add(CompanySheet.builder().companyName("位元組跳動").companyBoss("張一鳴").companyBase("北京市").companyDesc("位元組跳動的全球化佈局始於2015年 [3]  ,「技術出海」是位元組跳動全球化發展的核心戰略 [4]  ,其旗下產品有今日頭條、西瓜視訊、抖音、頭條百科、皮皮蝦、懂車帝、悟空問答等。").build());
        list.add(CompanySheet.builder().companyName("騰訊").companyBoss("馬化騰").companyBase("深圳市").companyDesc("社交和通訊服務QQ及微信/WeChat、社群網路平臺QQ空間、騰訊遊戲旗下QQ遊戲平臺、入口網站騰訊網、騰訊新聞使用者端和網路視訊服務騰訊視訊等。").build());
        list.add(CompanySheet.builder().companyName("百度").companyBoss("李彥宏").companyBase("北京市").companyDesc("百度(Baidu)是擁有強大網際網路基礎的領先AI公司。百度願景是:成為最懂使用者,並能幫助人們成長的全球頂級高科技公司。").build());
        return list;
    }
}

CitySheet.java

package com.example.springbooteasyexcel.sheet;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class CitySheet {
    @ExcelProperty(value = "城市名稱", index = 0)
    @ColumnWidth(10)
    private String cityName;

    @ExcelProperty(value = "城市介紹", index = 1)
    @ColumnWidth(60)
    private String cityDesc;

}

CompanySheet.java

package com.example.springbooteasyexcel.sheet;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class CompanySheet {

    @ExcelProperty(value = "公司名稱", index = 0)
    @ColumnWidth(10)
    private String companyName;

    @ExcelProperty(value = "公司創始人", index = 1)
    @ColumnWidth(10)
    private String companyBoss;

    @ExcelProperty(value = "公司總基地", index = 2)
    @ColumnWidth(10)
    private String companyBase;
    @ExcelProperty(value = "公司簡介", index = 3)
    @ColumnWidth(50)
    private String companyDesc;
}

UserSheet.java

package com.example.springbooteasyexcel.sheet;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.metadata.data.WriteCellData;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
public class UserSheet {

    @ExcelProperty(value = "使用者ID", index = 0)
    @ColumnWidth(10)
    private Long userId;

    @ExcelProperty(value = "使用者名稱稱", index = 1)
    @ColumnWidth(10)
    private String userName;

    @ExcelProperty(value = {"基本資訊", "手機號碼"}, index = 2)
    @ColumnWidth(20)
    private String userPhone;

    @ExcelProperty(value = {"基本資訊", "電子郵箱"}, index = 3)
    @ColumnWidth(20)
    private String userEmail;

    @ExcelProperty(value = {"基本資訊", "地址"}, index = 4)
    @ColumnWidth(20)
    private String userAddress;

    @ExcelProperty(value = "註冊時間", index = 5)
    @ColumnWidth(20)
    private Date registerTime;
    @ExcelProperty(value = "性別,男:紅色/女:綠色")
    @ColumnWidth(30)
    private WriteCellData<String> gender;
    /**
     * 忽略這個欄位
     */
    @ExcelIgnore
    private Integer age;
}

SpringBootEasyexcelApplication.java

package com.example.springbooteasyexcel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootEasyexcelApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootEasyexcelApplication.class, args);
    }
}

4.效果展示

單個sheet匯出

多個sheet匯出

5.總結

  • 1、Alibaba EasyExcel不僅支援寫Excel,還支援讀Excel和填充Excel,有興趣的話可以自己去研究,官網地址已經貼在上面了,我這裡只做一個引路的。
  • 2、常用註解有三個@ExcelProperty@ColumnWidth@ExcelIgnore。 (1)@ExcelProperty不僅確定表頭,還可以合併行,用法如下:
  @ExcelProperty(value = {"基本資訊", "手機號碼"}, index = 2)
    @ColumnWidth(20)
    private String userPhone;

    @ExcelProperty(value = {"基本資訊", "電子郵箱"}, index = 3)
    @ColumnWidth(20)
    private String userEmail;

    @ExcelProperty(value = {"基本資訊", "地址"}, index = 4)
    @ColumnWidth(20)
    private String userAddress;

效果如下:

  • (2)@ColumnWidth主要是控制列寬
  • (3)@ExcelIgnore忽略不需要輸出的欄位 3、寫有兩種形式 (1)寫到檔案
  /**
     * 最簡單的寫
     * <p>
     * 1. 建立excel對應的實體物件 參照{@link DemoData}
     * <p>
     * 2. 直接寫即可
     */
    @Test
    public void simpleWrite() {
        // 注意 simpleWrite在資料量不大的情況下可以使用(5000以內,具體也要看實際情況),資料量大參照 重複多次寫入

        // 寫法1 JDK8+
        // since: 3.0.0-beta1
        String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後檔案流會自動關閉
        // 如果這裡想使用03 則 傳入excelType引數即可
        EasyExcel.write(fileName, DemoData.class)
            .sheet("模板")
            .doWrite(() -> {
                // 分頁查詢資料
                return data();
            });

        // 寫法2
        fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 這裡 需要指定寫用哪個class去寫,然後寫到第一個sheet,名字為模板 然後檔案流會自動關閉
        // 如果這裡想使用03 則 傳入excelType引數即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

        // 寫法3
        fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 這裡 需要指定寫用哪個class去寫
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        }
    }

(2)寫到Web流,這裡的ContentType和CharacterEncoding不要亂碼,否則很容易亂碼或者檔案損壞

 /**
     * 檔案下載(失敗了會返回一個有部分資料的Excel)
     * <p>
     * 1. 建立excel對應的實體物件 參照{@link DownloadData}
     * <p>
     * 2. 設定返回的 引數
     * <p>
     * 3. 直接寫,這裡注意,finish的時候會自動關閉OutputStream,當然你外面再關閉流問題不大
     */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 這裡注意 有同學反應使用swagger 會導致各種問題,請直接用瀏覽器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 這裡URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關係
        String fileName = URLEncoder.encode("測試", "UTF-8").replaceAll("\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
    }

到此這篇關於SpringBoot整合EasyExcel實現Excel表格匯出功能的文章就介紹到這了,更多相關SpringBoot整合EasyExcel內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com