成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Boot 3.4 一鍵搞定!接口實現任意表的 Excel 導入導出

開發 前端
本文展示了一種高效且內存友好的 Excel 文件處理方案。無論是單一表格的導入導出,還是動態適配不同數據表的需求,我們都可以通過泛型和反射機制靈活實現。

在 Java Web 開發中,處理 Excel 文件的導入導出是常見且重要的需求,尤其是在大數據量的場景下,如何高效、安全地進行 Excel 文件的讀寫,直接影響到系統的性能與穩定性。傳統的工具如 EasyPoi 或 Hutool 提供了強大的功能,但在大規模數據處理時,這些工具常常面臨內存溢出(OOM)等性能瓶頸。為了解決這些問題,我們可以轉而使用 EasyExcel,它采用了低內存消耗的設計,能夠高效地處理海量數據的導入導出。

本文將介紹如何通過結合 Spring Boot 3.4 與 EasyExcel,實現一鍵搞定任意表的 Excel 導入導出。我們將通過使用 Java 8 的函數式編程特性、反射機制、以及多線程優化技術,進一步提升開發效率并確保系統的穩定性。特別地,在處理大數據量時,我們會通過批量存儲和線程池的方式,避免內存溢出問題,并進一步優化導入導出的性能。

優化策略

  1. 使用 Java 8 的函數式編程簡化數據導入
  2. 利用反射實現通用接口導入任意 Excel
  3. 通過線程池優化大數據量 Excel 導入性能
  4. 通過泛型支持多種數據導出格式

Maven 依賴

首先,需要在 pom.xml 文件中添加 EasyExcel 的依賴:

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

使用泛型實現對象的單個 Sheet 導入

首先,我們創建一個用于表示導入數據的類,假設是一個學生信息類:

package com.icoderoad.entity;


import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("stu_info")
@ApiModel("學生信息")
public class StuInfo {


    private static final long serialVersionUID = 1L;


    @ApiModelProperty("姓名")
    @ExcelProperty(value = "姓名", order = 0)
    private String name;


    @ApiModelProperty("年齡")
    @ExcelProperty(value = "年齡", order = 1)
    private Integer age;


    @ApiModelProperty("身高")
    @ExcelProperty(value = "身高", order = 2)
    private Double tall;


    @ApiModelProperty("自我介紹")
    @ExcelProperty(value = "自我介紹", order = 3)
    private String selfIntroduce;


    @ApiModelProperty("性別")
    @ExcelProperty(value = "性別", order = 4)
    private Integer gender;


    @ApiModelProperty("入學時間")
    @ExcelProperty(value = "入學時間", order = 5)
    private String intake;


    @ApiModelProperty("出生日期")
    @ExcelProperty(value = "出生日期", order = 6)
    private String birthday;
}

重寫 ReadListener 接口

為了處理數據導入過程中可能出現的內存溢出問題,我們重寫 ReadListener 接口,并將數據按批次進行存儲:

package com.icoderoad.listener;


import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.read.listener.ReadListener;
import com.icoderoad.exception.BizException;
import lombok.extern.slf4j.Slf4j;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;


@Slf4j
public class UploadDataListener<T> implements ReadListener<T> {


    private static final int BATCH_COUNT = 100;
    private List<T> cachedDataList = new ArrayList<>(BATCH_COUNT);
    private Predicate<T> predicate;
    private Consumer<Collection<T>> consumer;


    public UploadDataListener(Predicate<T> predicate, Consumer<Collection<T>> consumer) {
        this.predicate = predicate;
        this.consumer = consumer;
    }


    public UploadDataListener(Consumer<Collection<T>> consumer) {
        this.consumer = consumer;
    }


    @Override
    public void invoke(T data, AnalysisContext context) {
        if (predicate != null && !predicate.test(data)) {
            return;
        }
        cachedDataList.add(data);


        // When the batch size reaches BATCH_COUNT, trigger data storage
        if (cachedDataList.size() >= BATCH_COUNT) {
            try {
                consumer.accept(cachedDataList);
            } catch (Exception e) {
                log.error("Data upload failed! Data={}", cachedDataList);
                throw new BizException("Import failed");
            }
            cachedDataList.clear();
        }
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (!cachedDataList.isEmpty()) {
            try {
                consumer.accept(cachedDataList);
                log.info("All data parsing completed!");
            } catch (Exception e) {
                log.error("Data upload failed! Data={}", cachedDataList);
                if (e instanceof BizException) {
                    throw e;
                }
                throw new BizException("Import failed");
            }
        }
    }
}

Controller 層實現

在 Controller 層,我們使用 EasyExcel.read() 方法讀取上傳的文件,并通過 UploadDataListener 實現數據批量存儲:

package com.icoderoad.controller;


import com.alibaba.excel.EasyExcel;
import com.icoderoad.entity.StuInfo;
import com.icoderoad.listener.UploadDataListener;
import com.icoderoad.service.StuInfoService;
import com.icoderoad.util.ValidationUtils;
import com.icoderoad.exception.BizException;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;


import java.io.IOException;


@Slf4j
@RestController
@RequestMapping("/excel")
public class ExcelController {


    @Autowired
    private StuInfoService service;


    @ApiOperation("一鍵導入數據到 Excel")
    @PostMapping("/update")
    @ResponseBody
    public R<String> importExcel(MultipartFile file) throws IOException {
        try {
            EasyExcel.read(file.getInputStream(), StuInfo.class, new UploadDataListener<StuInfo>(
                list -> {
                    // 驗證數據
                    ValidationUtils.validate(list);
                    // 批量保存數據
                    service.saveBatch(list);
                    log.info("Imported {} rows of data from Excel", list.size());
                }
            )).sheet().doRead();
        } catch (IOException e) {
            log.error("Import failed", e);
            throw new BizException("Import failed");
        }
        return R.success("Success");
    }
}

處理任意數據表的導入

對于需要導入不同數據表的情況,我們可以通過傳遞表編碼以及文件來動態讀取數據,并進行適配:

@ApiOperation("通用數據表導入")
@PostMapping("/listenMapData")
@ResponseBody
public R<String> listenMapData(@RequestParam("tableCode") String tableCode, MultipartFile file) throws IOException {
    try {
        EasyExcel.read(file.getInputStream(), new NonClazzOrientedListener(
            list -> {
                log.info("Imported {} rows of data", list.size());
            }
        )).sheet().doRead();
    } catch (IOException e) {
        log.error("Import failed", e);
        throw new BizException("Import failed");
    }
    return R.success("Success");
}

重寫 ReadListener 接口處理非類型化數據

當我們需要處理不同類型的表時,可以通過 Map 來處理數據,具體實現如下:

package com.icoderoad.listener;


import com.alibaba.excel.context.AnalysisContext;
import com.icoderoad.exception.BizException;
import lombok.extern.slf4j.Slf4j;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;


@Slf4j
public class NonClazzOrientedListener implements ReadListener<Map<Integer, String>> {


    // 定義批次處理的大小
    private static final int BATCH_COUNT = 100;


    // 用于緩存行數據
    private List<List<Object>> rowsList = new ArrayList<>(BATCH_COUNT);


    // 臨時存儲每一行數據
    private List<Object> rowList = new ArrayList<>();


    // 條件判斷的 Predicate,決定是否處理當前行
    private Predicate<Map<Integer, String>> predicate;


    // 數據處理的消費者
    private Consumer<List> consumer;


    // 構造函數,傳入條件判斷和數據處理邏輯
    public NonClazzOrientedListener(Predicate<Map<Integer, String>> predicate, Consumer<List> consumer) {
        this.predicate = predicate;
        this.consumer = consumer;
    }


    // 構造函數,只傳入數據處理邏輯
    public NonClazzOrientedListener(Consumer<List> consumer) {
        this.consumer = consumer;
    }


    @Override
    public void invoke(Map<Integer, String> row, AnalysisContext analysisContext) {
        // 判斷是否符合處理條件,如果有定義 Predicate,進行過濾
        if (predicate != null && !predicate.test(row)) {
            return;
        }


        // 清理 rowList,為下一行做準備
        rowList.clear();


        // 處理每一行的數據,將行數據添加到 rowList
        row.forEach((k, v) -> {
            log.debug("處理數據行,鍵:{},值:{}", k, v);  // 中文日志輸出
            rowList.add(v == null ? "" : v);
        });


        // 將處理過的 rowList 添加到 rowsList
        rowsList.add(rowList);


        // 當達到批次大小時,執行存儲操作
        if (rowsList.size() >= BATCH_COUNT) {
            processBatch();
        }
    }


    // 批量處理數據,并清理緩存
    private void processBatch() {
        try {
            log.debug("執行存儲邏輯,當前批次包含 {} 行數據", rowsList.size());  // 中文日志輸出
            log.info("當前數據:{}", rowsList);
            consumer.accept(rowsList);
        } catch (Exception e) {
            log.error("數據上傳失?。祿簕}", rowsList, e);  // 中文日志輸出
            if (e instanceof BizException) {
                throw e;
            }
            throw new BizException("導入失敗");
        } finally {
            // 批次處理后清空緩存
            rowsList.clear();
        }
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 如果還有剩余數據沒有處理,執行最后一次存儲操作
        if (!rowsList.isEmpty()) {
            processBatch();
        }
        log.debug("所有數據處理并上傳完成。");  // 中文日志輸出
    }
}

結論

通過 EasyExcel 和 Spring Boot 3.4 的完美結合,本文展示了一種高效且內存友好的 Excel 文件處理方案。無論是單一表格的導入導出,還是動態適配不同數據表的需求,我們都可以通過泛型和反射機制靈活實現。同時,利用線程池的方式優化大數據量處理,顯著提高了性能,避免了內存溢出(OOM)問題。通過本文的方法,你可以輕松實現任意表的數據導入導出,滿足各種業務需求,并為未來的大規模數據處理奠定堅實的基礎。

優化后的這兩部分旨在加強對文章主題的深入闡述,同時突出技術的實際應用價值和解決方案的優勢,增強文章的專業性和實踐性。如果你覺得還有其他可以進一步擴展或調整的地方,隨時告訴我!

責任編輯:武曉燕 來源: 路條編程
相關推薦

2025-03-26 00:35:00

Javaweb開發

2025-04-08 01:00:00

Spring開發系統

2021-04-23 10:38:52

Spring BootSpringMVC源碼

2025-03-28 07:56:39

Spring服務配置

2020-03-31 15:03:56

Spring Boot代碼Java

2025-04-17 04:00:00

2025-02-17 00:00:45

接口支付寶沙箱

2024-10-17 11:09:46

2025-04-08 08:01:31

2022-06-06 08:42:04

spring-boo開發接口防盜刷

2025-04-27 03:00:00

Spring集成測試

2025-03-03 08:00:00

SpringBootEasyExcel數據導出

2021-05-14 06:15:48

SpringAware接口

2022-08-01 07:02:06

SpringEasyExcel場景

2012-01-10 15:35:44

金山快盤性能

2009-11-20 16:50:02

無線路由器

2020-06-22 07:55:28

接口爬蟲

2024-08-05 09:51:00

2023-07-18 17:59:38

2025-04-10 00:25:00

Spring@JsonView注解
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91在线观看视频 | 在线日韩视频 | 成人1区2区 | 国产精品视频网 | 人人干人人玩 | 成人福利视频网站 | 国产精品美女久久久久aⅴ国产馆 | 夜夜操av| 精品网 | 国产精品免费福利 | 午夜免费在线电影 | 蜜桃传媒av | 成人在线视频一区 | 欧美中文一区 | 久久aⅴ乱码一区二区三区 91综合网 | 青青草亚洲| 成人在线观看网站 | 久久久精品国产 | 精品在线一区二区三区 | 亚洲一区二区三区在线视频 | 久久精品国产99国产 | 激情久久久久 | 国产精品高潮呻吟久久av野狼 | www.久久.com | 久久999 | 国产美女在线播放 | 日批av| 国产成人99久久亚洲综合精品 | 精品视频在线免费观看 | 一区二区三区四区av | 日本免费一区二区三区四区 | 国产精品精品视频一区二区三区 | 亚洲人成人一区二区在线观看 | 国产在线麻豆精品入口 | 国产一区二区精品在线观看 | 精品欧美视频 | 一区二区三区亚洲视频 | av在线视 | av看片网站 | 久久精品一区二区三区四区 | 欧美国产精品 |