作者:風雨兼程
來源:jianshu.com/p/8f3defdc76d4
EasyExcel
在做excel匯入匯出的時候,發現專案中封裝的工具類及其難用,于是去gitHub上找了一些相關的框架,最終選定了EasyExcel,之前早有聽聞該框架,但是一直沒有去了解,這次借此學習一波,提高以后的作業效率,
實際使用中,發現是真的很easy,大部分api通過名稱就能知道大致意思,這點做的很nice,參考檔案,大部分場景的需求基本都能夠滿足,
GitHub上的官方說明

快速開始
maven倉庫地址
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.2</version>
</dependency>
推薦一個開源免費的 Spring Boot 最全教程:
https://github.com/javastacks/spring-boot-best-practice
匯入
如下圖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相關引數
needHead監聽器是否匯出頭,useDefaultStyle寫的時候是否是使用默認頭,- head 與clazz二選一,寫入檔案的頭串列,建議使用class,
autoTrim字串、表頭等資料自動trim,sheetNo需要寫入的編碼,默認0,sheetName需要些的Sheet名稱,默認同sheetNo,
總結
可以看出不管是excel的讀取還是寫入,都是一個注解加上一行代碼完成,可以讓我們少些很多決議的代碼,極大減少了重復的作業量,當然這兩個例子使用了最簡單的方式,EasyExcel還支持更多場景,例如讀,可以讀多個sheet,也可以決議一行資料或者多行資料做一次入庫操作;寫的話,支持復雜頭,指定列寫入,重復多次寫入,多個sheet寫入,根據模板寫入等等,
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
2.勁爆!Java 協程要來了,,,
3.Spring Boot 2.x 教程,太全了!
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/546939.html
標籤:Java
上一篇:2022SCNUOJ演算法課題目及解法總匯(JAVA)
下一篇:Mybatis資料庫驅動
