<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          EasyExcel,讓excel導(dǎo)入導(dǎo)出更加簡(jiǎn)單

          共 15244字,需瀏覽 31分鐘

           ·

          2021-07-30 22:36


          作者:風(fēng)雨兼程

          jianshu.com/p/8f3defdc76d4

          EasyExcel

          在做excel導(dǎo)入導(dǎo)出的時(shí)候,發(fā)現(xiàn)項(xiàng)目中封裝的工具類及其難用,于是去gitHub上找了一些相關(guān)的框架,最終選定了EasyExcel。之前早有聽聞該框架,但是一直沒有去了解,這次借此學(xué)習(xí)一波,提高以后的工作效率。

          實(shí)際使用中,發(fā)現(xiàn)是真的很easy,大部分api通過名稱就能知道大致意思,這點(diǎn)做的很nice。參考文檔,大部分場(chǎng)景的需求基本都能夠滿足。

          GitHub上的官方說明

          快速開始

          maven倉庫地址

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

          導(dǎo)入

          如下圖excel表格:

          建立導(dǎo)入對(duì)應(yīng)實(shí)體類

          @Data
          public class ReqCustomerDailyImport {
              /**
               * 客戶名稱
               */

              @ExcelProperty(index = 0)
              private String customerName;

              /**
               * MIS編碼
               */

              @ExcelProperty(index = 1)
              private String misCode;

              /**
               * 月度滾動(dòng)額
               */

              @ExcelProperty(index = 3)
              private BigDecimal monthlyQuota;

              /**
               * 最新應(yīng)收賬款余額
               */

              @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)
                      // 設(shè)置sheet,默認(rèn)讀取第一個(gè)
                      .sheet()
                      // 設(shè)置標(biāo)題所在行數(shù)
                      .headRowNumber(2)
                      .doReadSync()
          ;
          }

          運(yùn)行結(jié)果

          可以看出只需要在實(shí)體對(duì)象使用@ExcelProperty注解,讀取時(shí)指定該class,即可讀取,并且自動(dòng)過濾了空行,對(duì)于excel的讀取及其簡(jiǎn)單。不過此時(shí)發(fā)現(xiàn)一個(gè)問題,這樣我如果要校驗(yàn)字段該怎么辦?要將字段類型轉(zhuǎn)換成另外一個(gè)類型呢?

          不必?fù)?dān)心,我們可以想到的問題,作者肯定也考慮到了,下面來一個(gè)Demo

          代碼如下

          List<ReqCustomerDailyImport> reqCustomerDailyImports = EasyExcel.read(inputStream)
                      // 這個(gè)轉(zhuǎn)換是成全局的, 所有java為string,excel為string的都會(huì)用這個(gè)轉(zhuǎn)換器。
                      // 如果就想單個(gè)字段使用請(qǐng)使用@ExcelProperty 指定converter
                      .registerConverter(new StringConverter())
                      // 注冊(cè)監(jiān)聽器,可以在這里校驗(yàn)字段
                      .registerReadListener(new CustomerDailyImportListener())
                      .head(ReqCustomerDailyImport.class)
                      .sheet()
                      .headRowNumber(2)
                      .doReadSync()
          ;
          }

          監(jiān)聽器

          public class CustomerDailyImportListener extends AnalysisEventListener {

              List misCodes = Lists.newArrayList();

              /**
               * 每解析一行,回調(diào)該方法
               * @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編碼為空,請(qǐng)核實(shí)", context.readRowHolder().getRowIndex() + 1));
                  }
                  if (misCodes.contains(misCodes)) {
                      throw new RuntimeException(String.format("第%s行MIS編碼已重復(fù),請(qǐng)核實(shí)", context.readRowHolder().getRowIndex() + 1));
                  } else {
                      misCodes.add(misCode);
                  }
              }

              /**
               * 出現(xiàn)異常回調(diào)
               * @param exception
               * @param context
               * @throws Exception
               */

              @Override
              public void onException(Exception exception, AnalysisContext context) throws Exception {
                  // ExcelDataConvertException:當(dāng)數(shù)據(jù)轉(zhuǎn)換異常的時(shí)候,會(huì)拋出該異常,此處可以得知第幾行,第幾列的數(shù)據(jù)
                  if (exception instanceof ExcelDataConvertException) {
                      Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;
                      Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;
                      String message = "第" + rowIndex + "行,第" + columnIndex + "列" + "數(shù)據(jù)格式有誤,請(qǐng)核實(shí)";
                      throw new RuntimeException(message);
                  } else if (exception instanceof RuntimeException) {
                      throw exception;
                  } else {
                      super.onException(exception, context);
                  }
              }

              /**
               * 解析完全部回調(diào)
               * @param context
               */

              @Override
              public void doAfterAllAnalysed(AnalysisContext context) {
                  misCodes.clear();
              }
          }

          轉(zhuǎn)換器

          public class StringConverter implements Converter<String{

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

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

              /**
               * 將excel對(duì)象轉(zhuǎn)成Java對(duì)象,這里讀的時(shí)候會(huì)調(diào)用
               *
               * @param cellData            NotNull
               * @param contentProperty     Nullable
               * @param globalConfiguration NotNull
               * @return
               */

              @Override
              public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
                                              GlobalConfiguration globalConfiguration)
           
          {
                  return "自定義:" + cellData.getStringValue();
              }

              /**
               * 將Java對(duì)象轉(zhuǎn)成String對(duì)象,寫出的時(shí)候調(diào)用
               *
               * @param value
               * @param contentProperty
               * @param globalConfiguration
               * @return
               */

              @Override
              public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
                                                 GlobalConfiguration globalConfiguration)
           
          {
                  return new CellData(value);
              }
          }

          可以看出注冊(cè)了一個(gè)監(jiān)聽器:CustomerDailyImportListener,還注冊(cè)了一個(gè)轉(zhuǎn)換器:StringConverter。流程為:框架讀取一行數(shù)據(jù),先執(zhí)行轉(zhuǎn)換器,當(dāng)一行數(shù)據(jù)轉(zhuǎn)換完成,執(zhí)行監(jiān)聽器的回調(diào)方法,如果轉(zhuǎn)換的過程中,出現(xiàn)轉(zhuǎn)換異常,也會(huì)回調(diào)監(jiān)聽器中的onException方法。因此,可以在監(jiān)聽器中校驗(yàn)數(shù)據(jù),在轉(zhuǎn)換器中轉(zhuǎn)換數(shù)據(jù)類型或者格式。

          運(yùn)行結(jié)果

          修改一下表格,測(cè)試校驗(yàn)是否生效

          再次導(dǎo)入,查看運(yùn)行結(jié)果

          導(dǎo)入相關(guān)常用API

          注解

          • ExcelProperty 指定當(dāng)前字段對(duì)應(yīng)excel中的那一列。可以根據(jù)名字或者Index去匹配。當(dāng)然也可以不寫,默認(rèn)第一個(gè)字段就是index=0,以此類推。千萬注意,要么全部不寫,要么全部用index,要么全部用名字去匹配。千萬別三個(gè)混著用,除非你非常了解源代碼中三個(gè)混著用怎么去排序的。

          • ExcelIgnore 默認(rèn)所有字段都會(huì)和excel去匹配,加了這個(gè)注解會(huì)忽略該字段。

          • DateTimeFormat 日期轉(zhuǎn)換,用String去接收excel日期格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat。

          • NumberFormat 數(shù)字轉(zhuǎn)換,用String去接收excel數(shù)字格式的數(shù)據(jù)會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat。

          EasyExcel相關(guān)參數(shù)

          • readListener 監(jiān)聽器,在讀取數(shù)據(jù)的過程中會(huì)不斷的調(diào)用監(jiān)聽器。

          • converter 轉(zhuǎn)換器,默認(rèn)加載了很多轉(zhuǎn)換器。也可以自定義,如果使用的是registerConverter,那么該轉(zhuǎn)換器是全局的,如果要對(duì)單個(gè)字段生效,可以在ExcelProperty注解的converter指定轉(zhuǎn)換器。

          • headRowNumber 需要讀的表格有幾行頭數(shù)據(jù)。默認(rèn)有一行頭,也就是認(rèn)為第二行開始起為數(shù)據(jù)。

          • head 與clazz二選一。讀取文件頭對(duì)應(yīng)的列表,會(huì)根據(jù)列表匹配數(shù)據(jù),建議使用class。

          • autoTrim 字符串、表頭等數(shù)據(jù)自動(dòng)trim。

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

          • sheetName 根據(jù)名字去匹配Sheet,excel 2003不支持根據(jù)名字去匹配。

          導(dǎo)出

          建立導(dǎo)出對(duì)應(yīng)實(shí)體類

          @Data
          @Builder
          public class RespCustomerDailyImport {

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

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

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

              @ExcelProperty("最新應(yīng)收賬款余額")
              private BigDecimal accountReceivableQuota;

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

          Controller代碼

          @GetMapping("/export")
          public void export(HttpServletResponse response) throws IOException {
              // 生成數(shù)據(jù)
              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可以防止中文亂碼 當(dāng)然和easyexcel沒有關(guān)系
              String fileName = URLEncoder.encode("導(dǎo)出""UTF-8");
              response.setHeader("Content-disposition""attachment;filename=" + fileName + ".xlsx");
              EasyExcel.write(response.getOutputStream(), RespCustomerDailyImport.class)
                      .sheet("sheet0")
                      // 設(shè)置字段寬度為自動(dòng)調(diào)整,不太精確
                      .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                      .doWrite(respCustomerDailyImports)
          ;
          }

          導(dǎo)出效果

          導(dǎo)出相關(guān)常用API

          注解

          • ExcelProperty 指定寫到第幾列,默認(rèn)根據(jù)成員變量排序。value指定寫入的名稱,默認(rèn)成員變量的名字。

          • ExcelIgnore 默認(rèn)所有字段都會(huì)寫入excel,這個(gè)注解會(huì)忽略這個(gè)字段。

          • DateTimeFormat 日期轉(zhuǎn)換,將Date寫到excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.SimpleDateFormat。

          • NumberFormat 數(shù)字轉(zhuǎn)換,用Number寫excel會(huì)調(diào)用這個(gè)注解。里面的value參照java.text.DecimalFormat。

          EasyExcel相關(guān)參數(shù)

          • needHead 監(jiān)聽器是否導(dǎo)出頭。

          • useDefaultStyle 寫的時(shí)候是否是使用默認(rèn)頭。

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

          • autoTrim 字符串、表頭等數(shù)據(jù)自動(dòng)trim。

          • sheetNo 需要寫入的編碼。默認(rèn)0。

          • sheetName 需要些的Sheet名稱,默認(rèn)同sheetNo。

          總結(jié)

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

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

          程序汪資料鏈接

          歡迎添加程序汪個(gè)人微信 itwang007  進(jìn)粉絲群或圍觀朋友圈

          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋亚洲成人 | 夜夜骚视频网 | 国产微拍一区 | 国产高清性 | 一区二区三区无码播放 |