自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Boot 3.4 一鍵搞定!接口實(shí)現(xiàn)任意表的 Excel 導(dǎo)入導(dǎo)出

開發(fā) 前端
本文展示了一種高效且內(nèi)存友好的 Excel 文件處理方案。無論是單一表格的導(dǎo)入導(dǎo)出,還是動態(tài)適配不同數(shù)據(jù)表的需求,我們都可以通過泛型和反射機(jī)制靈活實(shí)現(xiàn)。

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

本文將介紹如何通過結(jié)合 Spring Boot 3.4 與 EasyExcel,實(shí)現(xiàn)一鍵搞定任意表的 Excel 導(dǎo)入導(dǎo)出。我們將通過使用 Java 8 的函數(shù)式編程特性、反射機(jī)制、以及多線程優(yōu)化技術(shù),進(jìn)一步提升開發(fā)效率并確保系統(tǒng)的穩(wěn)定性。特別地,在處理大數(shù)據(jù)量時(shí),我們會通過批量存儲和線程池的方式,避免內(nèi)存溢出問題,并進(jìn)一步優(yōu)化導(dǎo)入導(dǎo)出的性能。

優(yōu)化策略

  1. 使用 Java 8 的函數(shù)式編程簡化數(shù)據(jù)導(dǎo)入
  2. 利用反射實(shí)現(xiàn)通用接口導(dǎo)入任意 Excel
  3. 通過線程池優(yōu)化大數(shù)據(jù)量 Excel 導(dǎo)入性能
  4. 通過泛型支持多種數(shù)據(jù)導(dǎo)出格式

Maven 依賴

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

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

使用泛型實(shí)現(xiàn)對象的單個(gè) Sheet 導(dǎo)入

首先,我們創(chuàng)建一個(gè)用于表示導(dǎo)入數(shù)據(jù)的類,假設(shè)是一個(gè)學(xué)生信息類:

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("學(xué)生信息")
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("入學(xué)時(shí)間")
    @ExcelProperty(value = "入學(xué)時(shí)間", order = 5)
    private String intake;


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

重寫 ReadListener 接口

為了處理數(shù)據(jù)導(dǎo)入過程中可能出現(xiàn)的內(nèi)存溢出問題,我們重寫 ReadListener 接口,并將數(shù)據(jù)按批次進(jìn)行存儲:

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 層實(shí)現(xiàn)

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

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("一鍵導(dǎo)入數(shù)據(jù)到 Excel")
    @PostMapping("/update")
    @ResponseBody
    public R<String> importExcel(MultipartFile file) throws IOException {
        try {
            EasyExcel.read(file.getInputStream(), StuInfo.class, new UploadDataListener<StuInfo>(
                list -> {
                    // 驗(yàn)證數(shù)據(jù)
                    ValidationUtils.validate(list);
                    // 批量保存數(shù)據(jù)
                    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");
    }
}

處理任意數(shù)據(jù)表的導(dǎo)入

對于需要導(dǎo)入不同數(shù)據(jù)表的情況,我們可以通過傳遞表編碼以及文件來動態(tài)讀取數(shù)據(jù),并進(jìn)行適配:

@ApiOperation("通用數(shù)據(jù)表導(dǎo)入")
@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 接口處理非類型化數(shù)據(jù)

當(dāng)我們需要處理不同類型的表時(shí),可以通過 Map 來處理數(shù)據(jù),具體實(shí)現(xiàn)如下:

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;


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


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


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


    // 數(shù)據(jù)處理的消費(fèi)者
    private Consumer<List> consumer;


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


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


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


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


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


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


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


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


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

結(jié)論

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

優(yōu)化后的這兩部分旨在加強(qiáng)對文章主題的深入闡述,同時(shí)突出技術(shù)的實(shí)際應(yīng)用價(jià)值和解決方案的優(yōu)勢,增強(qiáng)文章的專業(yè)性和實(shí)踐性。如果你覺得還有其他可以進(jìn)一步擴(kuò)展或調(diào)整的地方,隨時(shí)告訴我!

責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2025-03-26 00:35:00

Javaweb開發(fā)

2025-04-08 01:00:00

Spring開發(fā)系統(tǒng)

2021-04-23 10:38:52

Spring BootSpringMVC源碼

2025-03-28 07:56:39

Spring服務(wù)配置

2020-03-31 15:03:56

Spring Boot代碼Java

2025-02-17 00:00:45

接口支付寶沙箱

2025-04-17 04:00:00

2024-10-17 11:09:46

2025-04-08 08:01:31

2022-06-06 08:42:04

spring-boo開發(fā)接口防盜刷

2025-03-03 08:00:00

SpringBootEasyExcel數(shù)據(jù)導(dǎo)出

2022-08-01 07:02:06

SpringEasyExcel場景

2021-05-14 06:15:48

SpringAware接口

2024-08-05 09:51:00

2020-06-22 07:55:28

接口爬蟲

2009-11-20 16:50:02

無線路由器

2012-01-10 15:35:44

金山快盤性能

2025-04-10 00:25:00

Spring@JsonView注解

2025-03-03 10:30:00

JavaExcelSpringBoot

2023-07-18 17:59:38

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號