Java 中EasyExcel的使用方式

背景

系統中經常要導出大量的數據,格式基本上都是Excel,然而每次導表都是對系統內存的一次挑戰。

在Java領域,生成或解析Excel的框架比較有名的當屬Apache的poi和jxl瞭。但使用它們,會面臨著嚴重的內存損耗問題。如果系統的並發量還不行,一旦導出大量數據,便會出現JVM頻繁full gc,甚至導致OOM。

EasyExcel是阿裡巴巴開源的一個Excel處理框架,使用簡單、節省內存。節省內存的原理也很簡單,在解析Excel時沒有將文件數據全部加載到內存當中,而是從磁盤文件中一行行讀取。

今天這篇文章就帶大傢來瞭解一下EasyExcel的使用,個人使用後的感慨是:太簡單易用瞭。

項目構建及依賴

首先創建一個Maven項目,在pom文件中添加如下依賴:

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

當引入該依賴之後,會發現在項目的依賴文件中同時多出瞭poi的類庫。也就是說,EasyExcel是基於poi來進行實現的,間接地引入瞭如下依賴:

<dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>3.17</version>
</dependency>
<dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-ooxml</artifactId>
     <version>3.17</version>
</dependency>

所以,當你的項目中已經引入瞭poi的依賴,要考慮一下版本的兼容問題。

創建實體類

EasyExcel易用性的體現之一就是可以通過在實體類中使用註解的形式,來與Excel中的表頭進行綁定。

現在直接上實體類:

@Data
public class UserData {
​
  @ExcelProperty(index = 0, value = "姓名")
  private String username;
​
  @ExcelProperty(index = 1, value = "年齡")
  private int age;
​
  @DateTimeFormat("yyyy-MM-dd")
  @ExcelProperty(index = 2, value = "生日")
  private Date birthday;
}

在上面的實體類中@Data為Lombok的註解,當然你可以自行生成getter/setter方法,其他的註解均為EasyExcel提供的:

  • @ExcelProperty:用於設置Excel表頭,其中index用戶表頭的編號,從0開始;value為表頭對應的內容。
  • @DateTimeFormat:用於日期的格式化。

完成上述功能準備工作之後,我們先來生成一個Excel。

生成Excel

下面直接展示生成Excel的示例代碼:

public class EasyExcelDemo {
  public static void main(String[] args) {
    // 實現excel寫操作
    //1.設置寫入文件夾地址和excel文件名稱
    String fileName = "/Users/zzs/temp/excel/write.xlsx";
    //調用easyExcel裡面的方法實現寫操作
    //2個參數,第一個參數是文件名稱,第二個參數是實體類
    EasyExcel.write(fileName, UserData.class).sheet("學生信息表").doWrite(getData());
  }
​
  //創建方法返回list集合
  public static List<UserData> getData() {
    List<UserData> list = new ArrayList<>();
​
    UserData userData1 = new UserData();
    userData1.setUsername("張三");
    userData1.setAge(22);
    userData1.setBirthday(formatDate("2000-10-11"));
    list.add(userData1);
​
    UserData userData2 = new UserData();
    userData2.setUsername("李四");
    userData2.setAge(23);
    userData2.setBirthday(formatDate("1999-5-3"));
    list.add(userData2);
    return list;
  }
  public static Date formatDate(String birthday) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");
    try {
      return sdf.parse(birthday);
    } catch (ParseException e) {
      e.printStackTrace();
    }
    return null;
  }
}

除瞭準備數據的代碼,核心代碼隻有main方法中調用的EasyExcel.write方法,就是如此的簡單。EasyExcel的write方法會根據傳入的數據和實體類UserData進行綁定,生成Excel文件。

我們來看一下Excel的效果:

生成效果還不錯,而且使用起來是不是非常簡單?

解析Excel

再來看看解析Excel的操作,直接用上面生成的Excel文件。

首先創建一個監聽器ExcelListener,集成EasyExcel提供AnalysisEventListener類:

public class ExcelListener extends AnalysisEventListener<UserData> {
  /**
   * 一行一行的讀取excel內容
   */
  @Override
  public void invoke(UserData data, AnalysisContext analysisContext) {
    System.out.println("****" + data);
  }

  /**
   * 讀取表頭內容
   */
  @Override
  public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    System.out.println("表頭" + headMap);
  }
  /**
   * 讀取完成操作
   */
  @Override
  public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    System.out.println("讀取Excel完畢");
  }
}

在該監聽器中,通過重寫AnalysisEventListener的方法來獲得解析的數據、表頭信息,以及解析完畢之後執行的操作信息。

同樣寫Excel一樣,通過EasyExcel類的靜態方法來執行讀操作:

public class EasyExcelReadDemo {

  public static void main(String[] args) {
    // 實現excel寫操作
    //1.設置寫入文件夾地址和excel文件名稱
    String fileName = "/Users/zzs/temp/excel/write.xlsx";
    //調用easyExcel裡面的方法實現寫操作
    //2個參數,第一個參數是文件名稱,第二個參數是實體類
    EasyExcel.read(fileName, UserData.class, new ExcelListener()).sheet().doRead();
  }
}

執行上述方法,打印信息如下:

表頭{0=姓名, 1=年齡, 2=生日}
****UserData(username=張三, age=22, birthday=Wed Oct 11 00:00:00 CST 2000)
****UserData(username=李四, age=23, birthday=Mon May 03 00:00:00 CST 1999)
讀取Excel完畢

最先是打印瞭表頭信息,這裡也可以看到表頭的排序是從0開始的。然後,讀取並打印瞭對應的Excel內容,兩條數據;最後,執行讀取完的方法中的日志打印。

看完瞭上面的整個操作,解析Excel是不是變得非常簡單瞭?再也不為解析Excel犯愁瞭。

其他相關特殊用法

上面提到的@DateTimeFormat註解可轉換日期格式,還有其他類似功能的註解和自定義轉換器。

自定義轉換器

通過自定義轉換器,比如將1、0轉換成男、女的實例:

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

public class SexConverter implements Converter<Integer> {

    @Override
    public Class<Integer> supportJavaTypeKey() {
        return Integer.class;
    }

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

    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return "男".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    public CellData<String> convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return new CellData<>(integer.equals(1) ? "男" : "女");
    }
}

性別屬性註入SexConverter轉換器:

@ExcelProperty(value = "性別", converter = SexConverter.class)
private Integer sex;

再次生成Excel,性別字段內容便顯示為:男、女字樣。

保留兩位小數

比如體重需要保留兩位小數,可通過@NumberFormat 註解實現:

@ExcelProperty(value = "體重KG")
@NumberFormat("0.##") // 會以字符串形式生成單元格,要計算的列不推薦
private BigDecimal weight;

另外一種方法是使用@ContentStyle註解:

@ContentStyle(dataFormat = 2)
private BigDecimal weight2;

這樣也能達到保留兩位小數的效果。

當然,也可以使用實現Converter接口的方式實現(同性別實現)。

排除指定Excel列

在很多場景下,Excel的列與實體類可能並不完全一致,這時就需要排除一些實體類的字段。

方式一:類上加註解 @ExcelIgnoreUnannotated,過濾屬性沒有@ExcelProperty註解的字段

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor  // 一定要有無參構造方法
@ExcelIgnoreUnannotated
public class UserData {
    .....
}

方式二:指定字段加@ExcelIgnore註解

@ExcelIgnore // 該字段不生成excel
private String remark;

方式三:代碼指定過濾字段,通過excludeColumnFiledNames方法:

    EasyExcel.write(fileName, UserData.class).sheet("學生信息表").excludeColumnFiledNames(Arrays.asList("remark")).doWrite(getData());

這種方法的好處是:同一Excel可以在調用方法時排除不同的數據列。

小結

本文介紹瞭EasyExcel的使用,整體而言操作簡單、使用方便,提供瞭不少註解,方便與實體對象之間的關系綁定。而且官網也提供瞭相關的性能數據,更多的API使用大傢還可以繼續探索。

無論從性能或易用性上來說,都值得你嘗試。特別是臨時寫一個Excel的解析或生成的工具,再也不用惆悵一行行的解析瞭,趕緊收藏用起來吧。

到此這篇關於Java 中EasyExcel的使用方式的文章就介紹到這瞭,更多相關Java 中EasyExcel內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: