EasyExcel,讓excel導入導出更加簡單

2022-02-07 我是程序汪
EasyExcel

在做excel導入導出的時候,發現項目中封裝的工具類及其難用,於是去gitHub上找了一些相關的框架,最終選定了EasyExcel。之前早有聽聞該框架,但是一直沒有去了解,這次藉此學習一波,提高以後的工作效率。

實際使用中,發現是真的很easy,大部分api通過名稱就能知道大致意思,這點做的很nice。參考文檔,大部分場景的需求基本都能夠滿足。

GitHub上的官方說明快速開始maven倉庫地址
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.2</version>
</dependency>

導入如下圖excel表格:建立導入對應實體類
@Data
public class ReqCustomerDailyImport {
    /**
     * 客戶名稱
     */
    @ExcelProperty(index = 0)
    private String customerName;

    /**
     * MIS編碼
     */
    @ExcelProperty(index = 1)
    private String misCode;

    /**
     * 月度滾動額
     */
    @ExcelProperty(index = 3)
    private BigDecimal monthlyQuota;

    /**
     * 最新應收帳款餘額
     */
    @ExcelProperty(index = 4)
    private BigDecimal accountReceivableQuota;

    /**
     * 本月利率(年化)
     */
    @ExcelProperty(index = 5)
    private BigDecimal dailyInterestRate;
}

Controller代碼
@PostMapping("/import")
public void importCustomerDaily(@RequestParam MultipartFile file) throws IOException {
    InputStream inputStream = file.getInputStream();
    List<ReqCustomerDailyImport> reqCustomerDailyImports = EasyExcel.read(inputStream)
            .head(ReqCustomerDailyImport.class)
            // 設置sheet,默認讀取第一個
            .sheet()
            // 設置標題所在行數
            .headRowNumber(2)
            .doReadSync();
}

運行結果

可以看出只需要在實體對象使用@ExcelProperty註解,讀取時指定該class,即可讀取,並且自動過濾了空行,對於excel的讀取及其簡單。不過此時發現一個問題,這樣我如果要校驗欄位該怎麼辦?要將欄位類型轉換成另外一個類型呢?

不必擔心,我們可以想到的問題,作者肯定也考慮到了,下面來一個Demo

代碼如下
List<ReqCustomerDailyImport> reqCustomerDailyImports = EasyExcel.read(inputStream)
            // 這個轉換是成全局的, 所有java為string,excel為string的都會用這個轉換器。
            // 如果就想單個欄位使用請使用@ExcelProperty 指定converter
            .registerConverter(new StringConverter())
            // 註冊監聽器,可以在這裡校驗欄位
            .registerReadListener(new CustomerDailyImportListener())
            .head(ReqCustomerDailyImport.class)
            .sheet()
            .headRowNumber(2)
            .doReadSync();
}

監聽器
public class CustomerDailyImportListener extends AnalysisEventListener {

    List misCodes = Lists.newArrayList();

    /**
     * 每解析一行,回調該方法
     * @param data
     * @param context
     */
    @Override
    public void invoke(Object data, AnalysisContext context) {
        String misCode = ((ReqCustomerDailyImport) data).getMisCode();
        if (StringUtils.isEmpty(misCode)) {
            throw new RuntimeException(String.format("第%s行MIS編碼為空,請核實", context.readRowHolder().getRowIndex() + 1));
        }
        if (misCodes.contains(misCodes)) {
            throw new RuntimeException(String.format("第%s行MIS編碼已重複,請核實", context.readRowHolder().getRowIndex() + 1));
        } else {
            misCodes.add(misCode);
        }
    }

    /**
     * 出現異常回調
     * @param exception
     * @param context
     * @throws Exception
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // ExcelDataConvertException:當數據轉換異常的時候,會拋出該異常,此處可以得知第幾行,第幾列的數據
        if (exception instanceof ExcelDataConvertException) {
            Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
            Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
            String message = "第" + rowIndex + "行,第" + columnIndex + "列" + "數據格式有誤,請核實";
            throw new RuntimeException(message);
        } else if (exception instanceof RuntimeException) {
            throw exception;
        } else {
            super.onException(exception, context);
        }
    }

    /**
     * 解析完全部回調
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        misCodes.clear();
    }
}

轉換器
public class StringConverter implements Converter<String> {

    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /**
     * 將excel對象轉成Java對象,這裡讀的時候會調用
     *
     * @param cellData            NotNull
     * @param contentProperty     Nullable
     * @param globalConfiguration NotNull
     * @return
     */
    @Override
    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
                                    GlobalConfiguration globalConfiguration) {
        return "自定義:" + cellData.getStringValue();
    }

    /**
     * 將Java對象轉成String對象,寫出的時候調用
     *
     * @param value
     * @param contentProperty
     * @param globalConfiguration
     * @return
     */
    @Override
    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
                                       GlobalConfiguration globalConfiguration) {
        return new CellData(value);
    }
}

可以看出註冊了一個監聽器:CustomerDailyImportListener,還註冊了一個轉換器:StringConverter。流程為:框架讀取一行數據,先執行轉換器,當一行數據轉換完成,執行監聽器的回調方法,如果轉換的過程中,出現轉換異常,也會回調監聽器中的onException方法。因此,可以在監聽器中校驗數據,在轉換器中轉換數據類型或者格式。

運行結果

修改一下表格,測試校驗是否生效

再次導入,查看運行結果

導入相關常用API註解

ExcelProperty 指定當前欄位對應excel中的那一列。可以根據名字或者Index去匹配。當然也可以不寫,默認第一個欄位就是index=0,以此類推。千萬注意,要麼全部不寫,要麼全部用index,要麼全部用名字去匹配。千萬別三個混著用,除非你非常了解原始碼中三個混著用怎麼去排序的。

ExcelIgnore 默認所有欄位都會和excel去匹配,加了這個註解會忽略該欄位。

DateTimeFormat 日期轉換,用String去接收excel日期格式的數據會調用這個註解。裡面的value參照java.text.SimpleDateFormat。

NumberFormat 數字轉換,用String去接收excel數字格式的數據會調用這個註解。裡面的value參照java.text.DecimalFormat。

EasyExcel相關參數

readListener 監聽器,在讀取數據的過程中會不斷的調用監聽器。

converter 轉換器,默認加載了很多轉換器。也可以自定義,如果使用的是registerConverter,那麼該轉換器是全局的,如果要對單個欄位生效,可以在ExcelProperty註解的converter指定轉換器。

headRowNumber 需要讀的表格有幾行頭數據。默認有一行頭,也就是認為第二行開始起為數據。

head 與clazz二選一。讀取文件頭對應的列表,會根據列表匹配數據,建議使用class。

autoTrim 字符串、表頭等數據自動trim。

sheetNo 需要讀取Sheet的編碼,建議使用這個來指定讀取哪個Sheet。

sheetName 根據名字去匹配Sheet,excel 2003不支持根據名字去匹配。

導出建立導出對應實體類
@Data
@Builder
public class RespCustomerDailyImport {

    @ExcelProperty("客戶編碼")
    private String customerName;

    @ExcelProperty("MIS編碼")
    private String misCode;

    @ExcelProperty("月度滾動額")
    private BigDecimal monthlyQuota;

    @ExcelProperty("最新應收帳款餘額")
    private BigDecimal accountReceivableQuota;

    @NumberFormat("#.##%")
    @ExcelProperty("本月利率(年化)")
    private BigDecimal dailyInterestRate;
}

Controller代碼
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
    // 生成數據
    List<RespCustomerDailyImport> respCustomerDailyImports = Lists.newArrayList();
    for (int i = 0; i < 50; i++) {
        RespCustomerDailyImport respCustomerDailyImport = RespCustomerDailyImport.builder()
                .misCode(String.valueOf(i))
                .customerName("customerName" + i)
                .monthlyQuota(new BigDecimal(String.valueOf(i)))
                .accountReceivableQuota(new BigDecimal(String.valueOf(i)))
                .dailyInterestRate(new BigDecimal(String.valueOf(i))).build();
        respCustomerDailyImports.add(respCustomerDailyImport);
    }
    
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding("utf-8");
    // 這裡URLEncoder.encode可以防止中文亂碼 當然和easyexcel沒有關係
    String fileName = URLEncoder.encode("導出", "UTF-8");
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    EasyExcel.write(response.getOutputStream(), RespCustomerDailyImport.class)
            .sheet("sheet0")
            // 設置欄位寬度為自動調整,不太精確
            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
            .doWrite(respCustomerDailyImports);
}

導出效果導出相關常用API註解

ExcelProperty 指定寫到第幾列,默認根據成員變量排序。value指定寫入的名稱,默認成員變量的名字。

ExcelIgnore 默認所有欄位都會寫入excel,這個註解會忽略這個欄位。

DateTimeFormat 日期轉換,將Date寫到excel會調用這個註解。裡面的value參照java.text.SimpleDateFormat。

NumberFormat 數字轉換,用Number寫excel會調用這個註解。裡面的value參照java.text.DecimalFormat。

EasyExcel相關參數

useDefaultStyle 寫的時候是否是使用默認頭。

head 與clazz二選一。寫入文件的頭列表,建議使用class。

autoTrim 字符串、表頭等數據自動trim。

sheetName 需要些的Sheet名稱,默認同sheetNo。

總結

可以看出不管是excel的讀取還是寫入,都是一個註解加上一行代碼完成,可以讓我們少些很多解析的代碼,極大減少了重複的工作量。當然這兩個例子使用了最簡單的方式,EasyExcel還支持更多場景,例如讀,可以讀多個sheet,也可以解析一行數據或者多行數據做一次入庫操作;寫的話,支持複雜頭,指定列寫入,重複多次寫入,多個sheet寫入,根據模板寫入等等。更多的例子可以去參考EasyExcel官方文檔

https://www.yuque.com/easyexcel/doc/easyexcel

相關焦點

  • 牛逼的EasyExcel,讓Excel導入導出更加簡單,附詳細教程!
    EasyExcel在做excel導入導出的時候,發現項目中封裝的工具類及其難用,於是去gitHub上找了一些相關的框架,最終選定了EasyExcel。之前早有聽聞該框架,但是一直沒有去了解,這次藉此學習一波,提高以後的工作效率。
  • Springboot整合easyExcel導入導出Excel
    、導出excel,因此在此記錄學習一下如何使用Springboot整合easyExcel;需求:資料庫中有張user表,有個業務要求可以導入、導出「用戶名單.xls」表一、準備:創建項目:關於springboot項目如何創建這裡不再贅述,放一張項目結構圖:1、導入easyexcel、mybatis、mysql依賴
  • easyExcel簡單實戰應用……
    前言在web開發中,我們經常會遇到數據導入或者導出excel的需求,如果用原生的poi(阿帕奇的頂級項目,項目地址:https://poi.apache.org/),雖然能夠滿足我們的需求,但是原生的接口用起來很不方便,數據格式都需要我們設定,而且它又是基於單元格的操作,在多個數據導出導入的場景下,每個場景都需要定製化的導出入方法,很不方便
  • Laravel 5使用Laravel Excel實現Excel/CSV文件導入導出的功能詳解
    1、簡介本文主要給大家介紹了關於Laravel 5用Laravel Excel實現Excel/CSV文件導入導出的相關內容,下面話不多說了,來一起看看詳細的介紹吧。2、安裝&配置使用Composer安裝依賴本文我們將在Laravel中使用Laravel Excel簡單實現Excel文件的導入和導出。
  • POI和 EasyExcel對Excel的簡單操作
    應用1.將用戶信息等文件導出為excel表格(導出數據)2.將用戶信息等文件從excel中導入到資料庫等(導入數據)ExcelExcel中的對象(1)工作簿:WorkBook(2)工作表:Sheet(3)行:
  • 如何使用JavaScript實現前端導入和導出excel文件
    使用JavaScript實現前端基於Table數據一鍵導出excel文件同樣的, 我們實現將table數據一鍵導出為excel也是類似, 不過方案有所不同, 我們先來看看在Dooring中的實現效果.2.1 一鍵導出為excel實現效果以上就是用戶基於後臺採集到的數據, 一鍵導出excel文件的流程, 最後一張圖是生成的excel文件在office軟體中的呈現.
  • 實現 Excel 導入導出
    日常在做後臺系統的時候會很頻繁的遇到Excel導入導出的問題,正好這次在做一個後臺系統,就想著寫一個公用工具來進行Excel的導入導出。一般我們在導出的時候都是導出的前端表格,而前端表格同時也會對應的在後臺有一個映射類。
  • 優雅 | 今天很水的文章-Excel導入導出
    前端部分這裡闡述前端部分導入,導出,生成Excel表格這裡使用的是一個js-xlsx插件,所以這裡進行嘗試。exceldownloadExl = () => { const { results } = this.props //需要導出的json數據 let datas = _.clone(results)//這裡為了不影響項目的數據的使用 採用了lodash中的深克隆方法 let json = datas.map(item=> { //將json
  • SpringBoot整合EasyPoi實現Excel的導入和導出(帶圖片)
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達66套java從入門到精通實戰課程分享前言實際工作中可能會用到Excel的導入和導出功能,Java操作Excel的方式有很多種,這裡簡單介紹一下如何使用EasyPoi來操作Excel。
  • 【R包薈萃】Excel文件的批量導入與導出:openxlsx包實用技巧
    本期主要為大家介紹導入和導出excel文件的工具:openxlsx包,以及批量導入導出文件的一些小技巧。install.packages("openxlsx")library("openxlsx")導出excel文件使用的是write.xlsx函數,通過介紹我們可以看到該函數主要是將數據框導出為xlsx文件,同時由於excel可存在多個sheet,該函數也支持導出a list of data.frames。
  • SpringBoot搭配EasyExcel,一切表格就這麼完美解決了~
    github地址:https://github.com/alibaba/easyexcel# EasyExcel控制表格註解@ContentRowHeight(int):設置 row 高度,不包含表頭標記在 類上@ContentRowHeight
  • 沒想到啊,Java操作Excel竟然這麼簡單!
    前言在工作中,使用excel表格處理數據是很常見的操作,本文就來講解下如何使用開源輪子實現下載、導入、導出的功能。項目介紹easyexcel是一款快速、簡單避免OOM的java處理Excel工具github地址:https://github.com/alibaba/easyexcelStart:15.2k
  • Hutool Java 工具類庫導出 Excel,超級簡單!
    來源:toutiao.com/i6771298852050829835前言在開發應用系統的時候,導出文件是必不可放的功能以前用過POI、easyexcel等工具的導入導出功能,但總感覺太麻煩了,代碼特別多,感覺並不是很好用。今天給大家介紹一款新工具,java工具類庫Hutool。
  • C 導出 Excel 的 6 種簡單方法!你會幾種?
    【CSDN 編者按】C# 導出 Excel 的6種簡單方法:數據表導出到 Excel,對象集合導出到 Excel,資料庫導出到 Excel,微軟網格控制項導出到 Excel,數組導出到 Excel,CSV 導出到 Excel,你都會了嗎?
  • EasyPOI:Excel/Word 導入/導出, 註解使用,完美,便捷,高效
    ,easyPoi 功能如同名字easy,主打的功能就是容易。讓一個沒接觸過poi的人員就可以方便的寫出Excel導出,Excel導出,Excel導入,Word模板導出,通過簡單的註解和模板語言(熟悉的表達式語法),完成以前複雜的寫法;    提供下官網地址,方便查閱最新動態以及細節問題:    關於easypoi可參考http://easypoi.mydoc.io/
  • 導出-excel圖表分析
    今天主要給大家介紹一下,導出excel的新圖表功能,以前導出excel的時候,都是默認出的柱形、餅形圖標,而且數據都默認的
  • Spring Boot使用EasyExecl導出Execl
    在做後臺管理系統的時候,很多場景都會遇到Execl的導入和導出,如果在以前的話,我們基本都是用POI這個組件,不得不說,這個組件的功能確實強大,但是也有有一些弊端,比如內存佔用高、文件過大會導致OOM。後來,阿里開源的EasyExecl就能很好解決這些問題。
  • 後端:Hutool Java 工具類庫導出 Excel,超級簡單!
    在開發應用系統的時候,導出文件是必不可放的功能。以前用過POI、easyexcel等工具的導入導出功能,但總感覺太麻煩了,代碼特別多,感覺並不是很好用。writer.merge(2, "申請人員信息"); // 一次性寫出內容,使用默認樣式,強制輸出標題 writer.write(list, true); //out為OutputStream,需要寫出到的目標流 //response為HttpServletResponse對象 response.setContentType("application/vnd.ms-excel
  • 使用Java進行excel操作的幾種方法 (下)
    dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>3.2 採用註解導出導入
  • 史上最全的Excel導入導出實戰,以後請別在百度了....
    喝水不忘挖井人,感謝阿里巴巴項目組提供了easyexcel工具類,github地址:https://github.com/alibaba/easyexcel文章目錄環境搭建 <dependency>   <groupId>com.alibaba</groupId>   <artifactId>