欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?EasyExcel導(dǎo)出合并單元格的示例詳解

 更新時(shí)間:2023年09月08日 08:38:48   作者:佛祖讓我來(lái)巡山  
EasyExcel是阿里巴巴開源的一個(gè)excel處理框架,以使用簡(jiǎn)單、節(jié)省內(nèi)存著稱,這篇文章主要為大家介紹了如何利用EasyExcel導(dǎo)出合并單元格,需要的可以參考下

前言

使用spring boot 對(duì)excel 進(jìn)行操作在平時(shí)項(xiàng)目中要經(jīng)常使用。常見(jiàn)通過(guò)jxl和poi 的方式進(jìn)行操作。但他們都存在一個(gè)嚴(yán)重的問(wèn)題就是非常的耗內(nèi)存。這里介紹一種 Easy Excel 工具來(lái)對(duì)excel進(jìn)行操作。

一、Easy Excel是什么

EasyExcel是阿里巴巴開源的一個(gè)excel處理框架,以使用簡(jiǎn)單、節(jié)省內(nèi)存著稱。easyExcel能大大減少占用內(nèi)存的主要原因是在解析Excel時(shí)沒(méi)有將文件數(shù)據(jù)一次性全部加載到內(nèi)存中,而是從磁盤上一行行讀取數(shù)據(jù),逐個(gè)解析。

二、使用EasyExcel 實(shí)現(xiàn)讀操作

從excel 中讀取數(shù)據(jù),常用的場(chǎng)景就是讀取excel的數(shù)據(jù),將相應(yīng)的數(shù)據(jù)保存到數(shù)據(jù)庫(kù)中。需要實(shí)現(xiàn)一定的邏輯處理。

1、導(dǎo)入依賴

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

2、創(chuàng)建讀取數(shù)據(jù)封裝類

@Data
public class User {
    @ExcelProperty(index = 0)
    private Integer id;
    @ExcelProperty(index = 1)
    private String name;
    @ExcelProperty(index = 2)
    private Integer age;
}

比如我們要讀取兩列的數(shù)據(jù),就寫兩個(gè)屬性。@ExcelProperty(index = 0)來(lái)設(shè)置要讀取的列,index=0表示讀取第一列。

3、創(chuàng)建讀取excel的監(jiān)聽(tīng)類

監(jiān)聽(tīng)器繼承 AnalysisEventListener 類

@Slf4j
public class UserExcelListener extends AnalysisEventListener<User> {
    /**
     * 解析excel文檔的每一行
     * @param user 參數(shù)user即是每行讀取數(shù)據(jù)轉(zhuǎn)換的User對(duì)象
     * @param analysisContext
     */
    @Override
    public void invoke(User user, AnalysisContext analysisContext){
        log.info("excel數(shù)據(jù)行:{}",user.toString());
    }
    /**
     * 整個(gè)文檔解析完執(zhí)行
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("文檔解析完畢");
    }
}

當(dāng)解析每一條數(shù)據(jù)時(shí)都會(huì)調(diào)用invoke方法,當(dāng)所有數(shù)據(jù)都解析完畢時(shí)最后會(huì)調(diào)用doAfterAllAnalysed方法??梢栽诒O(jiān)聽(tīng)類內(nèi)的方法中將每次讀取到的數(shù)據(jù)進(jìn)行保存或者其他操作處理。

4、接口使用easyExcel讀取excel文件調(diào)用監(jiān)聽(tīng)器

/**
     * 上傳excel文件并讀取其中內(nèi)容
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public String uploadExcel(MultipartFile file) {
        log.info("easyExcel上傳文件:{}", file);
        try {
            InputStream inputStream = file.getInputStream();
            EasyExcel.read(inputStream, User.class, <strong>new</strong><strong> UserExcelListener()</strong>)
                    .sheet()
                    .doRead();
        } catch (Exception e) {
        }
        return "表格文件上傳成功";
    }

三、使用EasyExcel 實(shí)現(xiàn)寫操作

寫操作有兩種寫法,一種是不創(chuàng)建對(duì)象的寫入,另一種是根據(jù)對(duì)象寫入。這里主要介紹創(chuàng)建對(duì)象寫入

創(chuàng)建對(duì)象寫入

1、創(chuàng)建excel對(duì)象類

@Data
public class User {
    @ExcelProperty(index = 0)
    private Integer id;
    @ExcelProperty(index = 1)
    private String name;
    @ExcelProperty(index = 2)
    private Integer age;
}

注意@ExcelProperty(“用戶編號(hào)”) 會(huì)生成相應(yīng)的列名為 用戶編號(hào),如果不設(shè)置,則會(huì)直接將字段名設(shè)置為excel的列名。

2、接口使用測(cè)試數(shù)據(jù)導(dǎo)出(常規(guī)導(dǎo)出不合并單元格)

/**
     * 輸出導(dǎo)出excel
     */
    @PostMapping("/export")
    public void export() {
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            user.setName("測(cè)試用戶-" + i);
            user.setAge(20 + i);
            users.add(user);
        }
        log.info("導(dǎo)出數(shù)據(jù)結(jié)果集:{}", users);
        String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\用戶信息表.xlsx";
        EasyExcel.write(fileName, User.class)
                .autoCloseStream(true)
                .sheet("sheet名稱")
                .doWrite(users);
    }

3、接口測(cè)試導(dǎo)出(單列合并單元格)

/**
     * 輸出導(dǎo)出excel
     */
    @PostMapping("/export1")
    public void export1() {
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            if (i == 3 || i == 4 || i == 5) {
                user.setName("測(cè)試用戶-3");
            } else {
                user.setName("測(cè)試用戶-" + i);
            }
            user.setAge(20 + i);
            users.add(user);
        }
        log.info("導(dǎo)出數(shù)據(jù)結(jié)果集:{}", users);
        String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\(單列相同內(nèi)容合并單元格)用戶信息表.xlsx";
        EasyExcel.write(fileName, User.class)
               <strong> .registerWriteHandler(</strong><strong>new</strong><strong> SimpleExcelMergeUtil())</strong>
                .autoCloseStream(true)
                .sheet("sheet名稱")
                .doWrite(users);
    }

如果要對(duì)導(dǎo)出的excel進(jìn)行處理,就需要自定義處理器類進(jìn)行處理

自定義easyExcel處理器(單列合并:根據(jù)用戶id相同的列進(jìn)行合并單元格):

/**
 * @version 1.0
 * @Package: com.stech.bms.buss.utils
 * @ClassName: ExcelMergeUtil
 * @Author: sgq
 * @Date: 2023/7/28 13:29
 * @Description: 僅處理單列數(shù)據(jù)相同合并單元格
 */
public class SimpleExcelMergeUtil implements CellWriteHandler {
    public SimpleExcelMergeUtil() {
    }
    /**
     * 創(chuàng)建每個(gè)單元格之前執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param row
     * @param head
     * @param columnIndex
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 創(chuàng)建每個(gè)單元格之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 每個(gè)單元格數(shù)據(jù)內(nèi)容渲染之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellData
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 每個(gè)單元格完全創(chuàng)建完之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellDataList
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 當(dāng)前行
        int curRowIndex = cell.getRowIndex();
        // 當(dāng)前列
        int curColIndex = cell.getColumnIndex();
        if (!isHead) {
            if (curRowIndex > 1 && curColIndex == 1) {
                // 從第二行數(shù)據(jù)行開始,獲取當(dāng)前行第二列數(shù)據(jù)
                Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
                // 獲取上一行第二列數(shù)據(jù)
                Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
                Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
                if (curData.equals(preData)) {
                    Sheet sheet = writeSheetHolder.getSheet();
                    List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
                    boolean isMerged = false;
                    for (int i = 0; i < mergedRegions.size() && !isMerged; i++) {
                        CellRangeAddress cellRangeAddr = mergedRegions.get(i);
                        // 若上一個(gè)單元格已經(jīng)被合并,則先移出原有的合并單元,再重新添加合并單元
                        if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                            sheet.removeMergedRegion(i);
                            cellRangeAddr.setLastRow(curRowIndex);
                            sheet.addMergedRegion(cellRangeAddr);
                            isMerged = true;
                        }
                    }
                    // 若上一個(gè)單元格未被合并,則新增合并單元
                    if (!isMerged) {
                        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                        sheet.addMergedRegion(cellRangeAddress);
                    }
                }
            }
        }
    }
}

4、接口測(cè)試導(dǎo)出(通用合并單元格)

/**
     * 輸出導(dǎo)出excel
     */
    @PostMapping("/export2")
    public void export2() {
        ArrayList<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setId(i);
            if (i == 3 || i == 4 || i == 5) {
                user.setName("測(cè)試用戶-3");
            } else {
                user.setName("測(cè)試用戶-" + i);
            }
            user.setAge(20 + i);
            users.add(user);
        }
        log.info("導(dǎo)出數(shù)據(jù)結(jié)果集:{}", users);
        // 從第幾行開始合并
        int mergeStartRowIndex = 5;
        // 需要合并哪些列
        int[] mergeColumns = {1};
        String fileName = "C:\\Users\\pytho\\Desktop\\fsdownload\\(單列相同內(nèi)容合并單元格-通用版)用戶信息表.xlsx";
        EasyExcel.write(fileName, User.class)
                .registerWriteHandler(new SimpleCommonExcelMergeUtil(mergeStartRowIndex,mergeColumns))
                .autoCloseStream(true)
                .sheet("sheet名稱")
                .doWrite(users);
    }

excel處理器類:

/**
 * @version 1.0
 * @Package: com.stech.bms.buss.utils
 * @ClassName: ExcelMergeUtil
 * @Author: sgq
 * @Date: 2023/7/28 13:29
 * @Description: 僅處理單列數(shù)據(jù)相同合并單元格
 */
public class SimpleCommonExcelMergeUtil implements CellWriteHandler {
    private int mergeStartRowIndex;
    private int[] mergeColumns;
    private List<Integer> mergeColumnList;
    public SimpleCommonExcelMergeUtil() {
    }
    public SimpleCommonExcelMergeUtil(int mergeStartRowIndex, int[] mergeColumns) {
        this.mergeStartRowIndex = mergeStartRowIndex;
        this.mergeColumns = mergeColumns;
        mergeColumnList = new ArrayList<>();
        for (int i : mergeColumns) {
            mergeColumnList.add(i);
        }
    }
    /**
     * 創(chuàng)建每個(gè)單元格之前執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param row
     * @param head
     * @param columnIndex
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 創(chuàng)建每個(gè)單元格之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 每個(gè)單元格數(shù)據(jù)內(nèi)容渲染之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellData
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
    }
    /**
     * 每個(gè)單元格完全創(chuàng)建完之后執(zhí)行
     *
     * @param writeSheetHolder
     * @param writeTableHolder
     * @param cellDataList
     * @param cell
     * @param head
     * @param relativeRowIndex
     * @param isHead
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 當(dāng)前行
        int curRowIndex = cell.getRowIndex();
        // 當(dāng)前列
        int curColIndex = cell.getColumnIndex();
        if (!isHead) {
            if (curRowIndex > mergeStartRowIndex && mergeColumnList.contains(curColIndex)) {
                // 從第二行數(shù)據(jù)行開始,獲取當(dāng)前行第二列數(shù)據(jù)
                Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
                // 獲取上一行第二列數(shù)據(jù)
                Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
                Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
                if (curData.equals(preData)) {
                    Sheet sheet = writeSheetHolder.getSheet();
                    List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
                    boolean isMerged = false;
                    for (int i = 0; i < mergedRegions.size() && !isMerged; i++) {
                        CellRangeAddress cellRangeAddr = mergedRegions.get(i);
                        // 若上一個(gè)單元格已經(jīng)被合并,則先移出原有的合并單元,再重新添加合并單元
                        if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                            sheet.removeMergedRegion(i);
                            cellRangeAddr.setLastRow(curRowIndex);
                            sheet.addMergedRegion(cellRangeAddr);
                            isMerged = true;
                        }
                    }
                    // 若上一個(gè)單元格未被合并,則新增合并單元
                    if (!isMerged) {
                        CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                        sheet.addMergedRegion(cellRangeAddress);
                    }
                }
            }
        }
    }
}

這只是簡(jiǎn)單的合并單元格例子,拋磚引玉的作用。工作中可能會(huì)遇到很多情況:合并單元格后第一列序列號(hào)也需要根據(jù)其他列進(jìn)行合并單元格且序列號(hào)還必須保持連續(xù),根據(jù)部分列合并單元格,隔行合并單元格等等情況,這就需要開發(fā)者對(duì)easyExcel的處理器類里面的api比較了解才能完成。遇到的問(wèn)題也可以留言,看到也會(huì)嘗試一起處理解決。

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

相關(guān)文章

  • Spring StopWatch使用實(shí)例詳解

    Spring StopWatch使用實(shí)例詳解

    這篇文章主要介紹了Spring StopWatch使用實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行

    Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行

    這篇文章主要介紹了Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • java實(shí)現(xiàn)日歷窗口小程序

    java實(shí)現(xiàn)日歷窗口小程序

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)日歷窗口小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Spring @Valid @Validated實(shí)現(xiàn)驗(yàn)證

    Spring @Valid @Validated實(shí)現(xiàn)驗(yàn)證

    這篇文章主要介紹了Spring @Valid @Validated實(shí)現(xiàn)驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • SpringBoot整合JWT的實(shí)現(xiàn)示例

    SpringBoot整合JWT的實(shí)現(xiàn)示例

    JWT是目前比較流行的跨域認(rèn)證解決方案,本文主要介紹了SpringBoot整合JWT的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • spring boot和spring cloud之間的版本關(guān)系

    spring boot和spring cloud之間的版本關(guān)系

    這篇文章主要介紹了spring boot和spring cloud之間的版本關(guān)系,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • Java參數(shù)傳遞實(shí)現(xiàn)代碼及過(guò)程圖解

    Java參數(shù)傳遞實(shí)現(xiàn)代碼及過(guò)程圖解

    這篇文章主要介紹了Java參數(shù)傳遞實(shí)現(xiàn)代碼及過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • MybatisPlus開啟二級(jí)緩存的方法詳解

    MybatisPlus開啟二級(jí)緩存的方法詳解

    這篇文章主要介紹了MybatisPlus開啟二級(jí)緩存的方法詳解,二級(jí)緩存是基于mapper文件的namespace級(jí)別,也就是說(shuō)多個(gè)sqlSession可以共享一個(gè)mapper中的二級(jí)緩存區(qū)域,需要的朋友可以參考下
    2023-11-11
  • Java 實(shí)戰(zhàn)練手項(xiàng)目之校園超市管理系統(tǒng)的實(shí)現(xiàn)流程

    Java 實(shí)戰(zhàn)練手項(xiàng)目之校園超市管理系統(tǒng)的實(shí)現(xiàn)流程

    讀萬(wàn)卷書不如行萬(wàn)里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+Mysql+Maven+Bootstrap實(shí)現(xiàn)一個(gè)校園超市管理系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-11-11
  • Java基于遞歸解決全排列問(wèn)題算法示例

    Java基于遞歸解決全排列問(wèn)題算法示例

    這篇文章主要介紹了Java基于遞歸解決全排列問(wèn)題算法,結(jié)合實(shí)例形式分析了Java使用遞歸算法解決全排列問(wèn)題的原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-11-11

最新評(píng)論