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

Java使用EasyExcel導出Excel的全流程詳解

 更新時間:2025年11月13日 08:41:46   作者:代碼怪獸大大作戰(zhàn)  
在 Java 后端開發(fā)中,Excel 導出是非常常見的需求,涉及報表、數據分析或業(yè)務導出,本文將結合 阿里 EasyExcel 庫,詳細講解 Excel 導出流程,包括工具類設計、樣式、列寬策略、對象映射、輸出流處理,需要的朋友可以參考下

引言

在 Java 后端開發(fā)中,Excel 導出是非常常見的需求,涉及報表、數據分析或業(yè)務導出。

本文將結合 阿里 EasyExcel 庫,詳細講解 Excel 導出流程,包括工具類設計、樣式、列寬策略、對象映射、輸出流處理

一、什么是 EasyExcel

EasyExcel 是阿里巴巴開源的 Java Excel 處理庫,相比 Apache POI 或 JXL:

高性能

  • 基于 SAX 流式解析和寫入,內存占用低
  • 可以輕松處理百萬行數據導出,不會 OOM

易用

  • 注解式對象映射(@ExcelProperty
  • 只需定義對象類即可生成 Excel 表格

樣式靈活

  • 表頭樣式、內容樣式、列寬、合并單元格等都可自定義

Web 集成方便

  • 可以直接輸出到瀏覽器下載,無需先生成文件

適合企業(yè)后臺系統(tǒng)、報表系統(tǒng)、統(tǒng)計平臺等場景。

二、Maven 依賴安裝

pom.xml 中引入 EasyExcel:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>4.0.3</version>
</dependency>

注意事項:

  1. EasyExcel 依賴 JDK 1.8 及以上版本
  2. 在實際項目中,如果出現依賴沖突或版本不兼容導致導出異常,可參考該文章進行排查和解決:EasyExcel 依賴沖突解決方案。

三、Excel 導出工具類設計

3.1 設計目標與整體架構

ExportTool 工具類主要實現了兩個導出方式:

  1. 簡單導出:快速將數據列表寫入 Excel,無樣式、無列寬控制。
  2. 帶樣式導出:支持自定義表頭樣式、內容樣式和列寬自適應。

核心設計特點

  • 泛型支持:通過 <T> + Class<T>,工具類可導出任意類型的數據對象。
  • 流安全處理:通過 NoCloseOutputStream 包裝輸出流,避免 EasyExcel 自動關閉 HttpServletResponse。
  • 策略模式:樣式、列寬分別封裝為獨立策略,便于擴展。
  • 日志和異常管理:導出過程中的異常統(tǒng)一記錄并封裝為 ServiceException,方便上層調用處理。

工具類結構可以理解為三層:

響應流處理 → ExcelWriter 構建 → 樣式 & 列寬策略 → 數據寫入

3.2 完整工具類:ExportTool.java

@Slf4j
public class ExportTool {
    /**
     * 導出 Excel 文件(簡單導出)
     *
     * @param dataList  導出的數據列表
     * @param clazz     數據類型(Excel 實體類)
     * @param fileName  導出的文件名(不帶擴展名)
     * @param <T>       數據泛型
     */
    public static <T> void exportExcel(List<T> dataList, Class<T> clazz, String fileName) {
        if (dataList == null || dataList.isEmpty()) {
            log.warn("[ExportTool] 導出數據為空: {}", fileName);
        }

        try {
            HttpServletResponse response = WebTool.getResponse();

            // 使用復用的響應流設置方法
            setupResponse(response, fileName + ".xlsx");

            try (ServletOutputStream out = response.getOutputStream()) {
                // 簡單導出,不增加樣式或列寬限制
                EasyExcel.write(out, clazz)
                        .autoCloseStream(true)
                        .sheet("數據")
                        .doWrite(dataList);
            }

            log.info("[ExportTool] 導出成功: {}, 共 {} 條記錄", fileName, dataList.size());
        } catch (Exception e) {
            log.error("[ExportTool] 導出失敗: {}", fileName, e);
            throw new ServiceException("導出失敗,請稍后重試");
        }
    }

    /**
     * 導出 Excel 文件(帶表頭樣式)
     *
     * @param dataList 數據列表
     * @param clazz    數據類型(Excel 實體類)
     * @param fileName 導出文件名(包含 .xlsx)
     * @param <T>      數據類型
     */
    public static <T> void exportExcelWithStyle(List<T> dataList, Class<T> clazz, String fileName) {
        HttpServletResponse response = WebTool.getResponse();
        try {
            // 設置響應頭
            setupResponse(response, fileName);

            // 獲取輸出流并防止 EasyExcel 關閉響應流
            ServletOutputStream out = response.getOutputStream();
            NoCloseOutputStream noCloseOut = new NoCloseOutputStream(out);

            // 創(chuàng)建 ExcelWriter
            ExcelWriter excelWriter = EasyExcel.write(noCloseOut, clazz)
                    // 注冊樣式策略
                    .registerWriteHandler(new HorizontalCellStyleStrategy(createHeadStyle(), createContentStyle()))
                    // 注冊列寬自適應策略
                    .registerWriteHandler(createAutoWidthStrategy())
                    .build();

            // 創(chuàng)建 Sheet
            WriteSheet writeSheet = EasyExcel.writerSheet("導出數據").build();

            // 寫入數據
            excelWriter.write(dataList, writeSheet);

            // 完成寫入
            excelWriter.finish();

            log.info("[ExportTool] 導出成功: {}", fileName);
        } catch (Exception e) {
            log.error("[ExportTool] 導出失敗: {}", fileName, e);
            throw new ServiceException("導出 Excel 失敗");
        }
    }

    /**
     * 設置 HttpServletResponse 響應頭
     */
    private static void setupResponse(HttpServletResponse response, String fileName) throws Exception {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
        response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
    }

    /**
     * 創(chuàng)建表頭樣式
     */
    private static WriteCellStyle createHeadStyle() {
        WriteCellStyle headStyle = new WriteCellStyle();

        // 設置白底(不填充顏色)
        headStyle.setFillForegroundColor(null);
        headStyle.setFillPatternType(FillPatternType.NO_FILL);

        // 設置字體
        WriteFont headFont = new WriteFont();
        headFont.setFontHeightInPoints((short) 11);
        headFont.setFontName("微軟雅黑");
        headFont.setBold(true);
        headFont.setColor(IndexedColors.PALE_BLUE.getIndex());
        headStyle.setWriteFont(headFont);

        // 設置居中與自動換行
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headStyle.setWrapped(true);

        // 設置無邊框
        headStyle.setBorderLeft(BorderStyle.NONE);
        headStyle.setBorderRight(BorderStyle.NONE);
        headStyle.setBorderTop(BorderStyle.NONE);
        headStyle.setBorderBottom(BorderStyle.NONE);

        return headStyle;
    }

    /**
     * 創(chuàng)建內容樣式
     */
    private static WriteCellStyle createContentStyle() {
        WriteCellStyle contentStyle = new WriteCellStyle();

        // 設置字體
        WriteFont contentFont = new WriteFont();
        contentFont.setFontHeightInPoints((short) 11);
        contentFont.setFontName("等線");
        contentStyle.setWriteFont(contentFont);

        // 內容右對齊,垂直居中,自動換行
        contentStyle.setHorizontalAlignment(HorizontalAlignment.RIGHT);
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        contentStyle.setWrapped(true);

        return contentStyle;
    }

    /**
     * 列寬自適應策略(根據內容長度自動調整列寬,限制最小 15、最大 20 字符)
     */
    private static AbstractColumnWidthStyleStrategy createAutoWidthStrategy() {
        return new AbstractColumnWidthStyleStrategy() {
            private final Map<Integer, Integer> columnWidthMap = new HashMap<>();

            @Override
            protected void setColumnWidth(WriteSheetHolder writeSheetHolder,
                                          List<WriteCellData<?>> cellDataList,
                                          Cell cell,
                                          Head head,
                                          Integer relativeRowIndex,
                                          Boolean isHead) {
                if (cell == null || cell.getCellType() != CellType.STRING) return;
                String value = cell.getStringCellValue();
                if (value == null) return;

                Sheet sheet = writeSheetHolder.getSheet();
                int columnIndex = cell.getColumnIndex();

                // 根據 UTF-8 字節(jié)長度計算列寬
                int length = value.getBytes(StandardCharsets.UTF_8).length;
                int minWidth = 15 * 256;
                int maxWidth = 20 * 256;
                int width = Math.max(minWidth, Math.min(length * 256 + 200, maxWidth));

                // 如果當前列寬大于記錄的最大寬度,則更新列寬
                Integer maxWidthRecorded = columnWidthMap.getOrDefault(columnIndex, 0);
                if (width > maxWidthRecorded) {
                    columnWidthMap.put(columnIndex, width);
                    sheet.setColumnWidth(columnIndex, width);
                }

                // 設置自動換行樣式
                CellStyle style = sheet.getWorkbook().createCellStyle();
                style.cloneStyleFrom(cell.getCellStyle());
                style.setWrapText(true);
                cell.setCellStyle(style);
            }
        };
    }

    /**
     * 包裝 Servlet 輸出流,不關閉底層流
     */
    public static class NoCloseOutputStream extends FilterOutputStream {
        public NoCloseOutputStream(OutputStream out) {
            super(out);
        }

        @Override
        public void close() throws IOException {
            flush(); // 不關閉底層流,僅刷新
        }
    }
}

3.3 核心方法分析

3.3.1 簡單導出方法

public static <T> void exportExcel(List<T> dataList, Class<T> clazz, String fileName)

功能:快速生成 Excel 文件,無樣式和列寬控制。

執(zhí)行流程

  1. 數據判空:日志告警數據為空情況。
  2. 獲取響應流:通過 WebTool.getResponse() 獲取 HttpServletResponse。
  3. 設置響應頭:設置 MIME 類型和 Content-Disposition,保證瀏覽器下載文件。
  4. EasyExcel 寫入:調用 EasyExcel.write(out, clazz).sheet("數據").doWrite(dataList),直接寫入數據列表。
  5. 異常處理:統(tǒng)一捕獲異常,拋出 ServiceException

特點

  • 接口簡單,使用泛型可適配任意實體類
  • 無額外樣式處理,適合數據量大或快速導出場景

3.3.2 帶樣式導出方法

public static <T> void exportExcelWithStyle(List<T> dataList, Class<T> clazz, String fileName)

功能:在 Excel 中生成 自定義表頭、內容樣式,并自動調整列寬。

執(zhí)行流程

  1. 獲取響應流并設置響應頭。
  2. 包裝輸出流:使用 NoCloseOutputStream 防止 EasyExcel 自動關閉底層流。
  3. 構建 ExcelWriter
    • 注冊 表頭樣式 + 內容樣式 (HorizontalCellStyleStrategy)
    • 注冊 列寬自適應策略 (AbstractColumnWidthStyleStrategy)
  4. 創(chuàng)建 Sheet:定義名稱,例如 "導出數據"。
  5. 寫入數據excelWriter.write(dataList, writeSheet)
  6. 完成寫入:調用 excelWriter.finish() 完成 Excel 寫入流程。
  7. 異常處理:統(tǒng)一日志記錄,拋出 ServiceException

特點

  • 樣式和列寬可獨立調整,便于復用和擴展
  • 保證瀏覽器端下載體驗不受影響
  • 對長文本和數字型列提供自動換行和右對齊

3.4 樣式策略分析

3.4.1 表頭樣式

WriteCellStyle headStyle = createHeadStyle();

特點

  • 字體:微軟雅黑,加粗,顏色淡藍
  • 對齊方式:水平居中、垂直居中
  • 自動換行,保證多行表頭顯示完整
  • 無邊框、白底,視覺簡潔

設計思路

  • 獨立封裝表頭樣式,便于在多個導出場景中復用
  • 樣式可通過 HorizontalCellStyleStrategy 與內容樣式統(tǒng)一管理

3.4.2 內容樣式

WriteCellStyle contentStyle = createContentStyle();

特點

  • 字體:等線
  • 水平右對齊(適合數字、金額)、垂直居中
  • 自動換行,保證長文本顯示
  • 與表頭樣式區(qū)分,形成信息層次感

設計思路

  • 獨立封裝內容樣式,提高可維護性
  • 通過策略模式應用于 ExcelWriter,無需重復設置

3.5 列寬自適應策略

AbstractColumnWidthStyleStrategy autoWidthStrategy = createAutoWidthStrategy();

實現邏輯

  1. 遍歷每個單元格內容,獲取 UTF-8 字節(jié)長度。
  2. 根據長度計算列寬,設置最小 15、最大 20 字符。
  3. 對比歷史最大列寬,確保列寬不會縮小。
  4. 設置單元格自動換行,保證內容完整顯示。

優(yōu)點

  • 避免手動設置每列寬度
  • 支持中英文混合內容,保證可讀性
  • 與樣式策略解耦,可獨立替換或增強

注意:UTF-8 字節(jié)長度計算對不同字符可能略有偏差,中英文混合時列寬可能需微調

3.6 輸出流包裝策略

public static class NoCloseOutputStream extends FilterOutputStream

作用

  • EasyExcel 在 finish() 默認關閉流
  • 包裝流后僅執(zhí)行 flush(),不關閉底層 HttpServletResponse
  • 保證瀏覽器下載文件正常,不中斷響應

優(yōu)點

  • 簡單、安全
  • 兼容大多數 Web 導出場景

3.7 ExcelWriter 構建與寫入

ExcelWriter excelWriter = EasyExcel.write(noCloseOut, clazz)
        .registerWriteHandler(styleStrategy)
        .registerWriteHandler(autoWidthStrategy)
        .build();

分析

  • 解耦樣式和列寬策略,便于單獨擴展
  • 批量寫入數據,可適配大數據場景
  • 支持自定義 Sheet 名稱
  • 可進一步擴展:凍結首行、單元格合并、插入公式等

四、導出對象類設計

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExportUserVO {

    @ExcelProperty("姓名")
    private String userName;

    @ExcelProperty("ID")
    private Long id;

    @ExcelProperty("使用個數")
    private Integer useCount;

}
  • 每個字段使用 @ExcelProperty 注解列名
  • 數據類型支持 String、Integer、Double、Long 等常用類型
  • 對象類清晰定義列名和數據結構

五、示例 Controller 導出方法

@GetMapping("/export")
public void exportUserExcel(HttpServletResponse response) {
    List<ExportUserVO> exportList = Arrays.asList(
        new ExportUserVO("張三", 1001L, 5),
        new ExportUserVO("李四", 1002L, 3)
    );

    String fileName = "用戶導出.xlsx";

    ExportTool.exportExcelWithStyle(response, exportList, ExportUserVO.class, fileName);
}
  • 瀏覽器訪問 /export 即可直接下載 Excel
  • 充分體現工具類的通用性和可復用性

導出Excel效果

以上就是Java使用EasyExcel導出Excel的全流程詳解的詳細內容,更多關于Java EasyExcel導出Excel的資料請關注腳本之家其它相關文章!

相關文章

  • SpringBoot整合MD5加密完成注冊和登錄方式

    SpringBoot整合MD5加密完成注冊和登錄方式

    MD5(MessageDigestAlgorithm5)是一種常見的哈希算法,用于生成固定長度(128位)的哈希值,主要應用于數據完整性校驗和密碼存儲,MD5具有快速計算、不可逆性和抗碰撞性等特點,盡管存在碰撞漏洞,MD5仍廣泛應用于文件下載校驗和數字簽名等場景
    2024-10-10
  • Java 重連機制的幾種實現方法

    Java 重連機制的幾種實現方法

    本文主要介紹了Java 重連機制的幾種實現方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2026-02-02
  • SpringMVC自定義日期轉換器方式

    SpringMVC自定義日期轉換器方式

    這篇文章主要介紹了SpringMVC如何自定義日期轉換器問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Java開發(fā)異步編程中常用的接口和類示例詳解

    Java開發(fā)異步編程中常用的接口和類示例詳解

    在現代Java中異步編程通常通過不同的機制和接口來實現,這篇文章主要介紹了Java開發(fā)異步編程中常用的接口和類的相關資料,文中通過代碼介紹的非常詳細,+需要的朋友可以參考下
    2025-08-08
  • IDEA 程序包不存在,找不到符號但是明明存在對應的jar包(問題分析及解決方案)

    IDEA 程序包不存在,找不到符號但是明明存在對應的jar包(問題分析及解決方案)

    這篇文章主要介紹了IDEA 程序包不存在,找不到符號但是明明存在對應的jar包 的解決方案,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Java隨機值設置(java.util.Random類或Math.random方法)

    Java隨機值設置(java.util.Random類或Math.random方法)

    在編程中有時我們需要生成一些隨機的字符串作為授權碼、驗證碼等,以確保數據的安全性和唯一性,這篇文章主要給大家介紹了關于Java隨機值設置的相關資料,主要用的是java.util.Random類或Math.random()方法,需要的朋友可以參考下
    2024-08-08
  • MyBatis插件機制超詳細講解

    MyBatis插件機制超詳細講解

    MyBatis在四大對象的創(chuàng)建過程中,都會有插件進行介入。插件可以利用動態(tài)代理機制一層層的包裝目標對象,而實現在目標對象執(zhí)行目標方法之前進行攔截的效果
    2022-11-11
  • 解決使用this.getClass().getResource()獲取文件時遇到的坑

    解決使用this.getClass().getResource()獲取文件時遇到的坑

    這篇文章主要介紹了解決使用this.getClass().getResource()獲取文件時遇到的坑問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 詳解Spring中實現接口動態(tài)的解決方法

    詳解Spring中實現接口動態(tài)的解決方法

    最近在工作遇到的一個,發(fā)現網上的資料較少,所以想著總結分享下,下面這篇文章主要給大家介紹了關于Spring中實現接口動態(tài)的解決方法,文中通過完整的示例代碼給大家介紹的非常詳細,對大家具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧。
    2017-07-07
  • Bloc事件流是一個阻塞隊列結論解析

    Bloc事件流是一個阻塞隊列結論解析

    這篇文章主要為大家介紹了Bloc事件流是一個阻塞隊列結論解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11

最新評論

昌黎县| 新晃| 淮安市| 新化县| 定安县| 开封市| 郴州市| 康保县| 乌海市| 南漳县| 曲松县| 长岭县| 汝州市| 西乡县| 天水市| 济阳县| 南安市| 海阳市| 滨州市| 高台县| 道孚县| 姚安县| 龙川县| 丘北县| 高陵县| 澄城县| 建瓯市| 承德县| 山阴县| 互助| 湛江市| 屏南县| 房山区| 黑山县| 闻喜县| 石首市| 铜鼓县| 延边| 汤阴县| 稻城县| 蓝田县|