最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java+EasyExcel實(shí)現(xiàn)單個(gè)接口導(dǎo)出多個(gè)Excel的示例詳解

 更新時(shí)間:2026年04月15日 08:37:58   作者:LucaJu  
在日常開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到?Excel?導(dǎo)出的需求,大多是單個(gè)接口導(dǎo)出單個(gè)?Excel?文件,今天就基于?Spring?Boot?+?EasyExcel分享一種簡(jiǎn)單、通用、可直接落地的實(shí)現(xiàn)方案,全程附完整代碼,新手也能快速上手

在日常開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到 Excel 導(dǎo)出的需求,大多是單個(gè)接口導(dǎo)出單個(gè) Excel 文件。但偶爾也會(huì)有特殊場(chǎng)景——需要一個(gè)接口同時(shí)導(dǎo)出兩個(gè)(或多個(gè))獨(dú)立的 Excel 文件,比如同時(shí)導(dǎo)出“用戶列表”和“訂單列表”,方便用戶一次性獲取完整數(shù)據(jù)。

今天就基于 Spring Boot + EasyExcel(目前主流穩(wěn)定版本),分享一種簡(jiǎn)單、通用、可直接落地的實(shí)現(xiàn)方案,全程附完整代碼,新手也能快速上手~

一、核心問(wèn)題與解決方案

首先要明確一個(gè)關(guān)鍵前提:HTTP 協(xié)議單次響應(yīng)只能返回一個(gè)字節(jié)流,無(wú)法直接返回兩個(gè)獨(dú)立的 Excel 文件(相當(dāng)于一次請(qǐng)求只能下載一個(gè)文件)。

那怎么實(shí)現(xiàn)“一個(gè)接口導(dǎo)出多個(gè) Excel”?答案很簡(jiǎn)單——將多個(gè) Excel 文件打包成 ZIP 壓縮包,接口返回 ZIP 流,用戶下載后解壓,就能得到多個(gè) Excel 文件。這是最通用、最合規(guī)的解決方案,既不違背 HTTP 協(xié)議,也能滿足用戶一次性獲取多份文件的需求。

二、前置準(zhǔn)備:引入依賴

首先在項(xiàng)目中引入 EasyExcel 核心依賴、ZIP 壓縮依賴(用于打包多文件),以及 Spring Web 依賴(接口開(kāi)發(fā)必備)。以下是 Maven 配置,Gradle 可對(duì)應(yīng)轉(zhuǎn)換,版本可根據(jù)自己項(xiàng)目需求微調(diào)(建議保持和示例一致,避免兼容問(wèn)題)。

<!-- EasyExcel 核心依賴 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
  	<version>4.0.3</version>
</dependency>
<!-- ZIP 壓縮依賴(處理多文件打包) -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.27.1</version>
</dependency>
<!-- Spring Web 依賴(已有則忽略,接口開(kāi)發(fā)必備) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

三、步驟1:定義Excel對(duì)應(yīng)的實(shí)體類

假設(shè)我們需要導(dǎo)出兩個(gè) Excel 文件:用戶列表訂單列表,分別創(chuàng)建對(duì)應(yīng)的實(shí)體類,通過(guò) EasyExcel 的 @ExcelProperty 注解指定 Excel 表頭名稱(注解參數(shù)就是最終 Excel 中顯示的表頭)。

實(shí)體類用 Lombok 的 @Data 注解簡(jiǎn)化 getter/setter 代碼,無(wú)需手動(dòng)編寫(xiě),節(jié)省開(kāi)發(fā)時(shí)間。

3.1 用戶實(shí)體類(UserData.java)

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * 用戶列表 Excel 對(duì)應(yīng)的實(shí)體類
 */
@Data
public class UserData {
    // Excel 表頭:用戶ID
    @ExcelProperty("用戶ID")
    private Long userId;

    // Excel 表頭:用戶名稱
    @ExcelProperty("用戶名稱")
    private String userName;

    // Excel 表頭:手機(jī)號(hào)
    @ExcelProperty("手機(jī)號(hào)")
    private String phone;
}

3.2 訂單實(shí)體類(OrderData.java)

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * 訂單列表 Excel 對(duì)應(yīng)的實(shí)體類
 */
@Data
public class OrderData {
    // Excel 表頭:訂單ID
    @ExcelProperty("訂單ID")
    private String orderId;

    // Excel 表頭:用戶ID(關(guān)聯(lián)用戶表)
    @ExcelProperty("用戶ID")
    private Long userId;

    // Excel 表頭:訂單金額
    @ExcelProperty("訂單金額")
    private Double amount;

    // Excel 表頭:創(chuàng)建時(shí)間
    @ExcelProperty("創(chuàng)建時(shí)間")
    private String createTime;
}

四、步驟2:封裝通用工具類(核心)

為了避免接口層代碼冗余,我們封裝一個(gè)工具類 ExcelZipExportUtil,專門(mén)處理“將多個(gè) Excel 寫(xiě)入 ZIP 流”和“初始化 HTTP 響應(yīng)頭”的邏輯。這個(gè)工具類是通用的,后續(xù)不管導(dǎo)出多少個(gè) Excel,都能直接復(fù)用,無(wú)需重復(fù)編碼。

import com.alibaba.excel.EasyExcel;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * EasyExcel 多文件導(dǎo)出 ZIP 工具類(通用可復(fù)用)
 */
public class ExcelZipExportUtil {

    /**
     * 將單個(gè) Excel 文件寫(xiě)入 ZIP 輸出流
     * @param zipOut ZIP 輸出流
     * @param excelFileName 單個(gè) Excel 的文件名(如:用戶列表.xlsx)
     * @param data Excel 中的數(shù)據(jù)列表
     * @param clazz Excel 對(duì)應(yīng)的實(shí)體類(用于解析表頭)
     */
    public static <T> void writeExcelToZip(ZipArchiveOutputStream zipOut, String excelFileName,
                                           List<T> data, Class<T> clazz) throws Exception {
        // 1. 臨時(shí)存儲(chǔ) Excel 內(nèi)容(內(nèi)存級(jí),不寫(xiě)入磁盤(pán),性能更高)
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        // 2. 使用 EasyExcel 寫(xiě)入數(shù)據(jù)(sheet1 是工作表名稱,可自定義)
        EasyExcel.write(bos, clazz)
          			// 不要自動(dòng)關(guān)閉,交給 Servlet 自己處理
                .autoCloseStream(false) 
          			// 基于 column 長(zhǎng)度,自動(dòng)適配。最大 255 寬度
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) 
          			// 避免 Long 類型丟失精度
                .registerConverter(new LongStringConverter()) 
          			// 工作表名稱
                .sheet("sheet1") 
                .doWrite(data);

        // 3. 將 Excel 作為 ZIP 的一個(gè)條目寫(xiě)入
        zipOut.putArchiveEntry(new ZipArchiveEntry(excelFileName));
        zipOut.write(bos.toByteArray());
        zipOut.closeArchiveEntry(); // 關(guān)閉當(dāng)前 ZIP 條目(必須,否則后續(xù)條目無(wú)法寫(xiě)入)

        // 4. 關(guān)閉臨時(shí)流
        bos.close();
    }

    /**
     * 初始化 HTTP 響應(yīng)頭(設(shè)置 ZIP 下載、解決中文文件名亂碼)
     * @param response 響應(yīng)對(duì)象
     * @param zipFileName 最終下載的 ZIP 壓縮包名稱(如:用戶訂單數(shù)據(jù).zip)
     */
    public static void initZipResponse(HttpServletResponse response, String zipFileName) throws Exception {
        // 設(shè)置響應(yīng)類型為 ZIP
        response.setContentType("application/zip");
        // 設(shè)置下載頭,URLEncoder.encode 解決中文文件名亂碼(兼容所有瀏覽器)
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
        // 禁止緩存(避免瀏覽器緩存舊文件)
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
    }
}

五、步驟3:接口層實(shí)現(xiàn)(最終落地)

創(chuàng)建 Controller 接口,模擬構(gòu)造兩個(gè) Excel 的測(cè)試數(shù)據(jù)(實(shí)際項(xiàng)目中,這里可以替換成從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)),然后調(diào)用上面封裝的工具類,將兩個(gè) Excel 打包成 ZIP 流,通過(guò) HttpServletResponse 返回,實(shí)現(xiàn)“一次請(qǐng)求下載兩個(gè) Excel”。

import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel 導(dǎo)出接口控制器
 */
@RestController
@RequestMapping("/export")
public class ExcelExportController {

    /**
     * 單個(gè)接口導(dǎo)出兩個(gè) Excel 文件(打包成 ZIP 下載)
     * 訪問(wèn)地址:http://localhost:8080/export/twoExcel
     */
    @GetMapping("/twoExcel")
    public void exportTwoExcel(HttpServletResponse response) {
        try {
            // 1. 初始化響應(yīng)頭,設(shè)置 ZIP 壓縮包名稱(用戶下載時(shí)顯示的文件名)
            ExcelZipExportUtil.initZipResponse(response, "用戶訂單數(shù)據(jù).zip");
            // 2. 獲取 HTTP 響應(yīng)輸出流,關(guān)聯(lián) ZIP 輸出流
            ServletOutputStream servletOut = response.getOutputStream();
            ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(servletOut);

            // 3. 構(gòu)造第一個(gè) Excel 的數(shù)據(jù)(用戶列表,實(shí)際項(xiàng)目中替換為數(shù)據(jù)庫(kù)查詢)
            List<UserData> userList = new ArrayList<>();
            // 3.1 完善數(shù)據(jù)邏輯省略

            // 4. 構(gòu)造第二個(gè) Excel 的數(shù)據(jù)(訂單列表,實(shí)際項(xiàng)目中替換為數(shù)據(jù)庫(kù)查詢)
            List<OrderData> orderList = new ArrayList<>();
						// 4.1 完善數(shù)據(jù)邏輯省略

            // 5. 關(guān)鍵操作:將兩個(gè) Excel 分別寫(xiě)入 ZIP 流
            ExcelZipExportUtil.writeExcelToZip(zipOut, "用戶列表.xlsx", userList, UserData.class);
            ExcelZipExportUtil.writeExcelToZip(zipOut, "訂單列表.xlsx", orderList, OrderData.class);

            // 6. 關(guān)閉流(順序不能錯(cuò)!否則 ZIP 包會(huì)損壞,無(wú)法解壓)
            zipOut.finish(); // 完成 ZIP 寫(xiě)入
            zipOut.close();
            servletOut.flush();
            servletOut.close();

        } catch (Exception e) {
            e.printStackTrace();
            // 實(shí)際項(xiàng)目中建議自定義異常處理,給前端返回明確的錯(cuò)誤提示
            response.setStatus(500);
        }
    }
}

六、關(guān)鍵注意事項(xiàng)(避坑重點(diǎn))

這部分一定要仔細(xì)看!很多新手實(shí)現(xiàn)后,出現(xiàn) ZIP 包損壞、中文亂碼、數(shù)據(jù)缺失等問(wèn)題,大多是因?yàn)楹雎粤诉@些細(xì)節(jié)。

  1. 流的關(guān)閉順序:必須先執(zhí)行 zipOut.closeArchiveEntry()(關(guān)閉當(dāng)前 ZIP 條目),再執(zhí)行 zipOut.finish(),最后關(guān)閉 ZIP 流和響應(yīng)流。順序顛倒會(huì)導(dǎo)致 ZIP 包損壞,無(wú)法解壓。
  2. 中文文件名亂碼:通過(guò) URLEncoder.encode(zipFileName, "UTF-8") 對(duì) ZIP 文件名和 Excel 文件名編碼,兼容 Chrome、Firefox、Edge 等所有主流瀏覽器,避免中文亂碼。
  3. Excel 寫(xiě)入方式:示例中使用 ByteArrayOutputStream 臨時(shí)存儲(chǔ) Excel 內(nèi)容,屬于內(nèi)存級(jí)寫(xiě)入,不占用磁盤(pán)空間,性能更高。不建議直接寫(xiě)入磁盤(pán)文件再打包,會(huì)增加磁盤(pán) IO 開(kāi)銷。
  4. 異常處理:實(shí)際項(xiàng)目中,建議替換 try-catch 中的 printStackTrace(),使用全局異常處理器,給前端返回明確的錯(cuò)誤信息(如“導(dǎo)出失敗,請(qǐng)重試”),提升用戶體驗(yàn)。

七、擴(kuò)展:導(dǎo)出更多Excel文件

如果需要導(dǎo)出 3 個(gè)、4 個(gè)甚至更多 Excel 文件,無(wú)需修改工具類,只需在接口中繼續(xù)調(diào)用 ExcelZipExportUtil.writeExcelToZip() 方法即可。

示例(新增“商品列表”Excel):

// 新增商品列表數(shù)據(jù)(假設(shè)已有 GoodsData 實(shí)體類)
List<GoodsData&gt; goodsList = new ArrayList<>();
// ... 構(gòu)造商品數(shù)據(jù)

// 新增一個(gè) Excel 寫(xiě)入 ZIP 流
ExcelZipExportUtil.writeExcelToZip(zipOut, "商品列表.xlsx", goodsList, GoodsData.class);

八、測(cè)試效果驗(yàn)證

代碼寫(xiě)完后,啟動(dòng) Spring Boot 項(xiàng)目,通過(guò)以下步驟測(cè)試,確保導(dǎo)出正常:

  1. 訪問(wèn)接口地址:http://localhost:8080/export/twoExcel(端口號(hào)根據(jù)自己項(xiàng)目配置調(diào)整);
  2. 瀏覽器會(huì)自動(dòng)彈出下載提示,下載的文件名為“用戶訂單數(shù)據(jù).zip”;
  3. 解壓 ZIP 壓縮包,會(huì)得到兩個(gè) Excel 文件:用戶列表.xlsx 和 訂單列表.xlsx;
  4. 打開(kāi) Excel 文件,檢查表頭和數(shù)據(jù)是否正常(和接口中構(gòu)造的測(cè)試數(shù)據(jù)一致)。

九、總結(jié)

代碼通用可復(fù)用,總結(jié)下來(lái)就是 3 步:

  1. 引入 EasyExcel 和 ZIP 依賴,做好前置準(zhǔn)備;
  2. 定義 Excel 對(duì)應(yīng)的實(shí)體類,封裝通用工具類(處理 ZIP 打包和響應(yīng)頭);
  3. 在接口中構(gòu)造數(shù)據(jù),調(diào)用工具類將多個(gè) Excel 寫(xiě)入 ZIP 流,返回給前端。

該方案適用于所有 Spring Boot 項(xiàng)目,支持任意數(shù)量的 Excel 導(dǎo)出,避開(kāi)了新手常踩的坑(流順序、中文亂碼),直接復(fù)制代碼就能落地使用。如果你的項(xiàng)目中有類似需求,不妨試試這個(gè)方案~

到此這篇關(guān)于Java+EasyExcel實(shí)現(xiàn)單個(gè)接口導(dǎo)出多個(gè)Excel的示例詳解的文章就介紹到這了,更多相關(guān)Java EasyExcel導(dǎo)出多個(gè)Excel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java接口中盡量避免使用數(shù)組

    Java接口中盡量避免使用數(shù)組

    這篇文章主要介紹了Java接口中盡量避免使用數(shù)組的建議,根據(jù)實(shí)際示例展示了接口中使用數(shù)組而造成的影響程序速度的現(xiàn)象,需要的朋友可以參考下
    2015-07-07
  • Springboot應(yīng)用中Mybatis輸出SQL日志的3種方法代碼示例

    Springboot應(yīng)用中Mybatis輸出SQL日志的3種方法代碼示例

    在前臺(tái)請(qǐng)求數(shù)據(jù)的時(shí)候,sql語(yǔ)句一直都是打印到控制臺(tái)的,有一個(gè)想法就是想讓它打印到日志里,該如何做呢?這篇文章主要給大家介紹了關(guān)于Springboot應(yīng)用中Mybatis輸出SQL日志的3種方法,需要的朋友可以參考下
    2024-01-01
  • Java?實(shí)戰(zhàn)項(xiàng)目之學(xué)生信息管理系統(tǒng)的實(shí)現(xiàn)流程

    Java?實(shí)戰(zhàn)項(xiàng)目之學(xué)生信息管理系統(tǒng)的實(shí)現(xiàn)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java的基礎(chǔ)語(yǔ)法學(xué)習(xí)筆記

    Java的基礎(chǔ)語(yǔ)法學(xué)習(xí)筆記

    這里為大家整理了Java的基礎(chǔ)語(yǔ)法學(xué)習(xí)筆記,包括關(guān)鍵詞、運(yùn)算符與基本的流程控制語(yǔ)句寫(xiě)法等,需要的朋友可以參考下
    2016-05-05
  • Java如何實(shí)現(xiàn)List自定義排序

    Java如何實(shí)現(xiàn)List自定義排序

    這篇文章主要介紹了Java如何實(shí)現(xiàn)List自定義排序,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-09-09
  • Spring boot中自定義Json參數(shù)解析器的方法

    Spring boot中自定義Json參數(shù)解析器的方法

    這篇文章主要介紹了Spring boot中自定義Json參數(shù)解析器的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Java使用反射生成JDK代理示例

    Java使用反射生成JDK代理示例

    這篇文章主要介紹了Java使用反射生成JDK代理,結(jié)合實(shí)例形式分析了java基于反射實(shí)現(xiàn)jdk動(dòng)態(tài)代理相關(guān)操作技巧,需要的朋友可以參考下
    2019-07-07
  • JDBC以反射機(jī)制加載類注冊(cè)驅(qū)動(dòng)連接MySQL

    JDBC以反射機(jī)制加載類注冊(cè)驅(qū)動(dòng)連接MySQL

    這篇文章介紹了JDBC以反射機(jī)制加載類注冊(cè)驅(qū)動(dòng)連接MySQL的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-01-01
  • Java如何設(shè)置過(guò)期時(shí)間的map的幾種方法

    Java如何設(shè)置過(guò)期時(shí)間的map的幾種方法

    本文主要介紹了Java如何設(shè)置過(guò)期時(shí)間的map的幾種方法,常見(jiàn)的解決方法有:ExpiringMap、LoadingCache及基于HashMap的封裝三種,下面就詳細(xì)的介紹一下,感興趣的可以了解下
    2022-03-03
  • Java異常處理實(shí)例詳解

    Java異常處理實(shí)例詳解

    這篇文章主要介紹了Java異常處理實(shí)例詳解,列舉了實(shí)際例子講解的很清晰,有感興趣的同學(xué)可以學(xué)習(xí)下
    2021-03-03

最新評(píng)論

收藏| 廉江市| 平阳县| 巴彦淖尔市| 即墨市| 灵台县| 古交市| 基隆市| 甘谷县| 苗栗市| 蒙城县| 即墨市| 南皮县| 江口县| 荔波县| 临泉县| 纳雍县| 剑河县| 綦江县| 武鸣县| 渝北区| 盐边县| 邹城市| 鄂温| 邯郸市| 汤原县| 鄯善县| 嘉兴市| 巫山县| 曲靖市| 青铜峡市| 静安区| 江都市| 神农架林区| 英吉沙县| 酒泉市| 鄂州市| 九寨沟县| 延川县| 牟定县| 临澧县|