採用註解方式的導入導出,能讓你在項目中更便捷的使用java
<dependency> <groupId>cn.gjing</groupId> <artifactId>tools-excel</artifactId> <version>1.1.4</version> </dependency>
實體上使用,聲明Excel與該實體存在映射,註解參數以下git
參數 | 描述 |
---|---|
value | Excel文件名,優先級低 於方法傳入 |
type | Excel文檔類型,默認XLS |
style | 導出的Excel樣式 |
字段上使用,聲明Excel的列表頭會與該字段映射,註解參數以下github
參數 | 描述 |
---|---|
value | 列表頭名字 |
pattern | 如何字段是時間類型的,且須要格式轉換,那麼則必定要設置 |
width | 這個列表頭單元格的寬度,默認20 * 256 ,建議設置256 的倍數 |
時間校驗註解,使用在字段上,代表在Excel文件
中這個列表頭下指定行數的單元格會添加時間的校驗,XLSX
類型文檔不支持,註解參數以下數組
參數 | 描述 |
---|---|
validClass | 校驗器Class |
boxLastRow | 數據校驗最多校驗多少行,默認是正文第一行 |
pattern | 校驗的時間格式,默認yyyy-MM-dd |
operatorType | 操做類型,默認OperatorType.BETWEEN |
expr1 | 表達式1,默認1970-01-01 |
expr2 | 表達式2,默認2999-01-01 |
showErrorBox | 是否彈出錯誤框,默認true |
showPromptBox | 是否當即彈出,默認true |
rank | 提示框級別,默認Rank.WARING 警告級別 |
errorTitle | 錯誤框標題 |
errorContent | 詳細錯誤內容 |
下拉框選值,使用在字段上,代表在Excel文件
中這個列表頭下指定行數的單元格會添加下拉框選項,註解參數以下app
參數 | 描述 |
---|---|
validClass | 校驗器Class |
combobox | 範圍值,數組類型 |
boxLastRow | 數據校驗最多校驗多少行,默認是該列表頭下的正文第一行 |
showErrorBox | 是否彈出錯誤框,默認true |
showPromptBox | 是否當即彈出,默認true |
rank | 提示框級別,默認Rank.WARING 警告級別 |
errorTitle | 錯誤框標題 |
errorContent | 詳細錯誤內容 |
數據類型校驗,使用在字段上,代表在Excel文件
中這個列表頭下指定行數的單元格會添加數據類型的校驗,註解參數以下ide
參數 | 描述 |
---|---|
validClass | 校驗器Class |
boxLastRow | 數據校驗最多校驗多少行,默認是該列表頭下的正文第一行 |
operatorType | 操做類型,默認OperatorType.GREATER_OR_EQUAL |
validType | 校驗類型,默認ValidType.INTEGER |
expr1 | 表達式1,在表達式2前面,默認0 |
expr2 | 表達式2,在操做類型爲BETWEEN 和NOT_BETWEEN 狀況下必填 |
showErrorBox | 是否彈出錯誤框,默認true |
showPromptBox | 是否當即彈出,默認true |
rank | 提示框級別,默認Rank.WARING 警告級別 |
errorTitle | 錯誤框標題 |
errorContent | 詳細錯誤內容 |
枚舉轉換器,使用在枚舉類型的字段上,註解參數以下this
參數 | 描述 |
---|---|
convert | 實現了EnumConvert 接口的類Class |
只須要在實體類的字段上加上對應註解便可excel
@Data //這是lombok的註解,幫完成get、set等方法 @Excel("用戶列表") public class User { @ExcelField("用戶Id") private Long id; @ExcelField("用戶名") private String userName; @ExcelField(value = "建立時間",pattern = "yyyy-MM-dd") private Date createTime; }
若是隻是導出Excel模板,只須要在write()
方法中傳入null便可code
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); ExcelFactory.createWriter(User.class, response) .write(users) .flush(); } }
排除某些帶了@ExcelField
註解的字段,只需在createWriter()
方法中ignores
參數指定你要忽略的字段,字段名要和實體一致
xml
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); ExcelFactory.createWriter(User.class, response,"id","createTime") .write(users) .flush(); } }
指定sheet導出,不指定的話會導出到默認名稱的sheet中
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); ExcelFactory.createWriter(User.class, response) .write(users,"用戶sheet") .flush(); } }
若是須要將不一樣規則數據導入到不一樣的sheet中或者是在同一個sheet中分層,能夠屢次調用write()
方法,若是不指定sheet的名稱則會寫入到默認名稱的sheet
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); List<User> users2 = userService.userList(); ExcelFactory.createWriter(User.class, response) .write(users) .write(users2,"sheet2") .flush(); } }
指定大標題,該操做要在每次調用write()
以前,僅對本次調用有效
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); ExcelFactory.createWriter(User.class, response) //指定大標題佔用的行數和內容 .bigTitle(() -> new BigTitle(3,"我是大標題")) .write(users) .flush(); } }
重置當前導出的Excel與實體的關聯,該操做要在調用write()
以前
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userService; @Resource private OrderService orderService; @GetMapping("/user") @ApiOperation(value = "導出用戶") public void exportUser(HttpServletResponse response) { List<User> users = userService.userList(); List<Order> orderList = orderService.orderList(); ExcelFactory.createWriter(User.class, response) .write(users) //重置,若是要忽略某些字段不導出,在該方法 ignores 參數指定便可 //注意點和上文介紹的導出一致 .resetExcelClass(Order.class) .write(orderList) .flush(); } }
在整個鏈式調用完畢後,必定要在最後調用flush()
方法,不然數據不會寫入到Excel文件中
經過get()
方法獲取導入的數據,該方法爲最終操做
,整個導入會結束
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userSerivce; @PostMapping("/user_import") @ApiOperation("導入") public ResponseEntity userImport(MultipartFile file) throws IOException { List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class) //不指定sheet名稱,會去讀默認名稱的sheet .read() .get(); userService.saveUserList(users); return ResponseEntity.ok("導入成功"); } }
不少時候咱們數據太多而寫在了不一樣的sheet中,這時可能須要分開讀取每一個sheet,使用上面的get()
方法獲取導入的結果確定是不行的,這時能夠經過建立監聽者去監聽每次導入的結果並執行對應的操做
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userSerivce; @PostMapping("/user_import") @ApiOperation("導入") public ResponseEntity userImport(MultipartFile file) throws IOException { ExcelFactory.createReader(file.getInputStream(), User.class) .read() .listener(e -> userService.saveUserList(e)) .read("sheet2") .listener(e -> userService.saveUserList(e)); return ResponseEntity.ok("導入成功"); } }
導入的Excel存在大標題的話,須要指定列表頭開始的下標,對應Excel文件列表頭那一行左邊的數字序號,該操做要在調用read()
前進行
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userSerivce; @PostMapping("/user_import") @ApiOperation("導入") public ResponseEntity userImport(MultipartFile file) throws IOException { List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class) .headerIndex(2) .read() .get(); userService.saveUserList(users); return ResponseEntity.ok("導入成功"); } }
限制讀取行數,該行數爲你要截止讀取到Excel文件的哪一行(包括本行),該操做要在調用read()
前進行
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userSerivce; @PostMapping("/user_import") @ApiOperation("導入") public ResponseEntity userImport(MultipartFile file) throws IOException { List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class) .endIndex(5) .read() .get(); userService.saveUserList(users); return ResponseEntity.ok("導入成功"); } }
實體存在枚舉類型的字段,在導入導出時須要指定枚舉轉換器,實現EnumConvert
接口並重寫其中方法,導出會顯示你指定的內容,導入會轉爲指定的枚舉
/** * 性別 * * @author Gjing **/ @Getter public enum GenderEnum { MAN(1, "男"), WOMAN(2, "女"); private int type; private String desc; GenderEnum(int type, String desc) { this.type = type; this.desc = desc; } /** * excel導入的時候轉換成枚舉用的 * * @param s desc * @return GenderEnum */ public static GenderEnum of(String s) { for (GenderEnum genderEnum : GenderEnum.values()) { if (genderEnum.getDesc().equals(s)) { return genderEnum; } } throw new NullPointerException("沒找到你的枚舉"); } /** * 自定義枚舉轉換器 */ public static class MyExcelEnumConvert implements EnumConvert<GenderEnum, String> { @Override public GenderEnum toEntityAttribute(String s) { return of(s); } @Override public String toExcelAttribute(GenderEnum genderEnum) { return genderEnum.desc; } } }
字段使用註解並指定你的轉換類
@Excel("用戶列表") public class User { @ExcelField("性別") @ExcelEnumConvert(convert = GenderEnum.MyExcelEnumConvert.class) private GenderEnum genderEnum; }
在導出模板時,有時候須要讓用戶填寫指定規則的內容,這時能夠進行數據格式校驗
@Excel("用戶列表") public class User { @ExcelField("用戶Id") private Long id; @ExcelField("用戶名") //在當前列表頭下方的十行單元格進行數據校驗,必須輸入長度大於等於3的文本 @NumericValid(validationType = TEXT_LENGTH, expr1 = "3", operatorType = GREATER_OR_EQUAL,boxLastRow = 10) private String userName; @ExcelField("性別") //指定該列表頭下的第一行單元格只能選取這兩個值 @ExplicitValid(combobox = {"男","女"}) @ExcelEnumConvert(convert = GenderEnum.MyExcelEnumConvert.class) private GenderEnum genderEnum; @ExcelField(value = "建立時間",pattern = "yyyy-MM-dd") //在當前列表頭下方的第一行單元格中,時間只能輸入在2019-10-11至2019-10-13範圍的時間 @DateValid(expr1 = "2019-10-11",expr2 = "2019-10-13") private Date createTime; }
以前講解的使用的一些功能都是採起默認實現的,有時候有點本身的想法,也能夠進行自定義其中的功能
實現ExcelStyle
接口,裏面有設置大標題、正文、列表頭樣式的三個方法,選擇你要自定義的方法進行重寫便可,如下演示了自定義大標題樣式
/** * 本身定義的樣式 * @author Gjing **/ public class MyExcelStyle implements ExcelStyle { @Override public CellStyle setTitleStyle(CellStyle cellStyle) { cellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.index); cellStyle.setAlignment(HorizontalAlignment.CENTER); cellStyle.setWrapText(true); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); return cellStyle; } }
在對應實體@Excel
註解指定你自定義的樣式類
@Excel(value = "用戶列表",style = MyExcelStyle.class) public class User { @ExcelField("用戶Id") private Long id; @ExcelField("用戶名") private String userName; @ExcelField(value = "建立時間",pattern = "yyyy-MM-dd") private Date createTime; }
經過上文的介紹,知道一共有三種校驗的註解,這三個註解分別均可以自定義實現邏輯,實現ExcelValidation
接口,並選擇你須要自定義哪些註解的邏輯
/** * 本身實現校驗邏輯 * @author Gjing **/ public class MyValid implements ExcelValidation { @Override public void valid(DateValid dateValid, Sheet sheet, int firstRow, int firstCol, int lastCol) { } @Override public void valid(NumericValid numericValid, Sheet sheet, int firstRow, int firstCol, int lastCol) { } @Override public void valid(ExplicitValid explicitValid, Workbook workbook, Sheet sheet, int firstRow, int firstCol, int lastCol) { } }
修改你實體類中對應註解的默認處理類validClass
,沒有指定的註解仍是會走默認處理
@Excel("用戶列表") public class User { @ExcelField("用戶Id") private Long id; @ExcelField("用戶名") @NumericValid(validClass = MyValid.class,validationType = TEXT_LENGTH, expr1 = "3", operatorType = GREATER_OR_EQUAL,boxLastRow = 10) private String userName; @ExcelField(value = "建立時間",pattern = "yyyy-MM-dd") //在當前列表頭下方的第一行單元格中,時間只能輸入在2019-10-11至2019-10-13範圍的時間 @DateValid(expr1 = "2019-10-11",expr2 = "2019-10-13") private Date createTime; }
自定義導出的核心處理器,需實現ExcelWriterResolver
接口並重寫其中的方法
/** * 自定義導出處理器 * @author Gjing **/ public class MyWriteResolver implements ExcelWriterResolver { @Override public void write(List<?> list, Workbook workbook, String s, List<Field> list1, MetaStyle metaStyle, BigTitle bigTitle) { } @Override public void flush(HttpServletResponse httpServletResponse, String s) { } }
在導出時重置處理器,該方法要在你全部鏈式操做以前
調用
/** * @author Gjing **/ @RestController @Api(tags = "用戶") public class UserController { @Resource private UserService userService; @GetMapping("/user") @ApiOperation(value = "使用自定義的導出處理器導出用戶模板") public void exportUser(HttpServletResponse response) { ExcelFactory.createWriter(User.class, response) //重置處理器 .resetResolver(MyWriteResolver::new) .write(null) .flush(); } }
自定義導入處理器,需實現ExcelReaderResolver
接口並重寫其中的方法
/** * 自定義導入處理器 * @author Gjing **/ public class MyReaderResolver implements ExcelReaderResolver { @Override public void read(InputStream inputStream, Class<?> aClass, Listener<List<Object>> listener, int i, int i1, String s) { } }
在導出的方法中重置處理器,該操做要在全部鏈式操做前
調用
/** * @author Gjing **/ @RestController public class UserController { @Resource private UserService userSerivce; @PostMapping("/user_import") @ApiOperation("導入") public ResponseEntity userImport(MultipartFile file) throws IOException { ExcelFactory.createReader(file.getInputStream(), User.class) .resetResolver(MyReaderResolver::new) .read() .listener(e -> userService.saveUserList(e)) return ResponseEntity.ok("導入成功"); } }
源代碼地址:tools-excel
Demo地址:excel-demo