SpringBoot整合EasyExcel?3.x的完整示例
1 EasyExcel 3.x
1.1 簡(jiǎn)介
EasyExcel
是一個(gè)基于 Java 的、快速、簡(jiǎn)潔、解決大文件內(nèi)存溢出的 Excel
處理工具。它能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成 Excel 的讀、寫(xiě)等功能。
EasyExcel
文檔地址:https://easyexcel.opensource.alibaba.com/
1.2 引入依賴
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.3</version> </dependency>
1.3 簡(jiǎn)單導(dǎo)出
1.3.1 定義實(shí)體類(lèi)
在EasyExcel
中,以面向?qū)ο笏枷雭?lái)實(shí)現(xiàn)導(dǎo)入導(dǎo)出,無(wú)論是導(dǎo)入數(shù)據(jù)還是導(dǎo)出數(shù)據(jù)都可以想象成具體某個(gè)對(duì)象的集合,所以為了實(shí)現(xiàn)導(dǎo)出用戶信息功能,首先創(chuàng)建一個(gè)用戶對(duì)象UserDO實(shí)體類(lèi),用于封裝用戶信息:
@Data public class UserDO { @ExcelProperty("用戶編號(hào)") @ColumnWidth(20) private Long id; @ExcelProperty("用戶名") @ColumnWidth(20) private String username; @ExcelIgnore private String password; @ExcelProperty("昵稱") @ColumnWidth(20) private String nickname; @ExcelProperty("生日") @ColumnWidth(20) @DateTimeFormat("yyyy-MM-dd") private Date birthday; @ExcelProperty("手機(jī)號(hào)") @ColumnWidth(20) private String phone; @ExcelProperty("身高(米)") @NumberFormat("#.##") @ColumnWidth(20) private Double height; @ExcelProperty(value = "性別", converter = GenderConverter.class) @ColumnWidth(10) private Integer gender; }
上面代碼中類(lèi)屬性上使用了EasyExcel
核心注解:
@ExcelProperty
:核心注解,value
屬性可用來(lái)設(shè)置表頭名稱,converter
屬性可以用來(lái)設(shè)置類(lèi)型轉(zhuǎn)換器;@ColumnWidth
:用于設(shè)置表格列的寬度;@DateTimeFormat
:用于設(shè)置日期轉(zhuǎn)換格式;@NumberFormat
:用于設(shè)置數(shù)字轉(zhuǎn)換格式。
1.3.2 自定義轉(zhuǎn)換器
在EasyExcel
中,如果想實(shí)現(xiàn)枚舉類(lèi)型到字符串類(lèi)型轉(zhuǎn)換(例如gender屬性:1 -> 男,2 -> 女),需實(shí)現(xiàn)Converter
接口來(lái)自定義轉(zhuǎn)換器,下面為自定義GenderConverter
性別轉(zhuǎn)換器代碼實(shí)現(xiàn):
public class GenderConverter implements Converter<Integer> { @Override public Class<?> supportJavaTypeKey() { return Integer.class; } @Override public CellDataTypeEnum supportExcelTypeKey() { return CellDataTypeEnum.STRING; } @Override public Integer convertToJavaData(ReadConverterContext<?> context) { return GenderEnum.convert(context.getReadCellData().getStringValue()).getValue(); } @Override public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) { return new WriteCellData<>(GenderEnum.convert(context.getValue()).getDescription()); } }
性別枚舉
@Getter @AllArgsConstructor public enum GenderEnum { /** * 未知 */ UNKNOWN(0, "未知"), /** * 男性 */ MALE(1, "男性"), /** * 女性 */ FEMALE(2, "女性"); private final Integer value; @JsonFormat private final String description; public static GenderEnum convert(Integer value) { return Stream.of(values()) .filter(bean -> bean.value.equals(value)) .findAny() .orElse(UNKNOWN); } public static GenderEnum convert(String description) { return Stream.of(values()) .filter(bean -> bean.description.equals(description)) .findAny() .orElse(UNKNOWN); } }
1.3.3 定義接口
@RestController @RequestMapping("/excel") public class ExcelController { @GetMapping("/export/user") public void exportUserExcel(HttpServletResponse response) { try { this.setExcelResponseProp(response, "用戶列表"); List<UserDO> userList = this.getUserList(); EasyExcel.write(response.getOutputStream()) .head(UserDO.class) .excelType(ExcelTypeEnum.XLSX) .sheet("用戶列表") .doWrite(userList); } catch (IOException e) { throw new RuntimeException(e); } } /** * 設(shè)置響應(yīng)結(jié)果 */ private void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(rawFileName, "UTF-8").replaceAll("\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); } /** * 讀取用戶列表數(shù)據(jù) */ private List<UserDO> getUserList() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); ClassPathResource classPathResource = new ClassPathResource("mock/users.json"); InputStream inputStream = classPathResource.getInputStream(); return objectMapper.readValue(inputStream, new TypeReference<List<UserDO>>() { }); } }
1.4 簡(jiǎn)單導(dǎo)入
@RestController @RequestMapping("/excel") @Api(tags = "EasyExcel") public class ExcelController { @PostMapping("/import/user") public ResponseVO importUserExcel(@RequestPart(value = "file") MultipartFile file) { try { List<UserDO> userList = EasyExcel.read(file.getInputStream()) .head(UserDO.class) .sheet() .doReadSync(); return ResponseVO.success(userList); } catch (IOException e) { return ResponseVO.error(); } } }
1.5 復(fù)雜導(dǎo)出
1.5.1 引言
由于 EasyPoi
支持嵌套對(duì)象導(dǎo)出,直接使用內(nèi)置 @ExcelCollection
注解即可實(shí)現(xiàn),遺憾的是 EasyExcel
不支持一對(duì)多導(dǎo)出,只能自行實(shí)現(xiàn),通過(guò)此issues了解到,項(xiàng)目維護(hù)者建議通過(guò)自定義合并策略方式來(lái)實(shí)現(xiàn)一對(duì)多導(dǎo)出。
解決思路:只需把訂單主鍵相同的列中需要合并的列給合并了,就可以實(shí)現(xiàn)這種一對(duì)多嵌套信息的導(dǎo)出
1.5.2 自定義注解
創(chuàng)建一個(gè)自定義注解,用于標(biāo)記哪些屬性需要合并單元格,哪個(gè)屬性是主鍵,用于判斷是否需要合并以及合并的主鍵
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExcelMerge { /** * 是否合并單元格 * * @return true || false */ boolean merge() default true; /** * 是否為主鍵(即該字段相同的行合并) * * @return true || false */ boolean isPrimaryKey() default false; }
1.5.3 定義實(shí)體類(lèi)
在需要合并單元格的屬性上設(shè)置 @ExcelMerge
注解,二級(jí)表頭通過(guò)設(shè)置 @ExcelProperty
注解中 value
值為數(shù)組形式來(lái)實(shí)現(xiàn)該效果:
@Data public class OrderBO { @ExcelProperty(value = "訂單主鍵") @ColumnWidth(16) @ExcelMerge(merge = true, isPrimaryKey = true) private String id; @ExcelProperty(value = "訂單編號(hào)") @ColumnWidth(20) @ExcelMerge(merge = true) private String orderId; @ExcelProperty(value = "收貨地址") @ExcelMerge(merge = true) @ColumnWidth(20) private String address; @ExcelProperty(value = "創(chuàng)建時(shí)間") @ColumnWidth(20) @DateTimeFormat("yyyy-MM-dd HH:mm:ss") @ExcelMerge(merge = true) private Date createTime; @ExcelProperty(value = {"商品信息", "商品編號(hào)"}) @ColumnWidth(20) private String productId; @ExcelProperty(value = {"商品信息", "商品名稱"}) @ColumnWidth(20) private String name; @ExcelProperty(value = {"商品信息", "商品標(biāo)題"}) @ColumnWidth(30) private String subtitle; @ExcelProperty(value = {"商品信息", "品牌名稱"}) @ColumnWidth(20) private String brandName; @ExcelProperty(value = {"商品信息", "商品價(jià)格"}) @ColumnWidth(20) private BigDecimal price; @ExcelProperty(value = {"商品信息", "商品數(shù)量"}) @ColumnWidth(20) private Integer count; }
1.5.4 數(shù)據(jù)映射與平鋪
導(dǎo)出之前,需要對(duì)數(shù)據(jù)進(jìn)行處理,將訂單數(shù)據(jù)進(jìn)行平鋪,orderList
為平鋪前格式,exportData
為平鋪后格式:
1.5.5 自定義單元格合并策略
當(dāng) Excel 中兩列主鍵相同時(shí),合并被標(biāo)記需要合并的列:
public class ExcelMergeStrategy implements RowWriteHandler { /** * 主鍵下標(biāo) */ private Integer primaryKeyIndex; /** * 需要合并的列的下標(biāo)集合 */ private final List<Integer> mergeColumnIndexList = new ArrayList<>(); /** * 數(shù)據(jù)類(lèi)型 */ private final Class<?> elementType; public ExcelMergeStrategy(Class<?> elementType) { this.elementType = elementType; } @Override public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { // 判斷是否為標(biāo)題 if (isHead) { return; } // 獲取當(dāng)前工作表 Sheet sheet = writeSheetHolder.getSheet(); // 初始化主鍵下標(biāo)和需要合并字段的下標(biāo) if (primaryKeyIndex == null) { this.initPrimaryIndexAndMergeIndex(writeSheetHolder); } // 判斷是否需要和上一行進(jìn)行合并 // 不能和標(biāo)題合并,只能數(shù)據(jù)行之間合并 if (row.getRowNum() <= 1) { return; } // 獲取上一行數(shù)據(jù) Row lastRow = sheet.getRow(row.getRowNum() - 1); // 將本行和上一行是同一類(lèi)型的數(shù)據(jù)(通過(guò)主鍵字段進(jìn)行判斷),則需要合并 if (lastRow.getCell(primaryKeyIndex).getStringCellValue().equalsIgnoreCase(row.getCell(primaryKeyIndex).getStringCellValue())) { for (Integer mergeIndex : mergeColumnIndexList) { CellRangeAddress cellRangeAddress = new CellRangeAddress(row.getRowNum() - 1, row.getRowNum(), mergeIndex, mergeIndex); sheet.addMergedRegionUnsafe(cellRangeAddress); } } } /** * 初始化主鍵下標(biāo)和需要合并字段的下標(biāo) * * @param writeSheetHolder WriteSheetHolder */ private void initPrimaryIndexAndMergeIndex(WriteSheetHolder writeSheetHolder) { // 獲取當(dāng)前工作表 Sheet sheet = writeSheetHolder.getSheet(); // 獲取標(biāo)題行 Row titleRow = sheet.getRow(0); // 獲取所有屬性字段 Field[] fields = this.elementType.getDeclaredFields(); // 遍歷所有字段 for (Field field : fields) { // 獲取@ExcelProperty注解,用于獲取該字段對(duì)應(yīng)列的下標(biāo) ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); // 判斷是否為空 if (null == excelProperty) { continue; } // 獲取自定義注解,用于合并單元格 ExcelMerge excelMerge = field.getAnnotation(ExcelMerge.class); // 判斷是否需要合并 if (null == excelMerge) { continue; } for (int i = 0; i < fields.length; i++) { Cell cell = titleRow.getCell(i); if (null == cell) { continue; } // 將字段和表頭匹配上 if (excelProperty.value()[0].equalsIgnoreCase(cell.getStringCellValue())) { if (excelMerge.isPrimaryKey()) { primaryKeyIndex = i; } if (excelMerge.merge()) { mergeColumnIndexList.add(i); } } } } // 沒(méi)有指定主鍵,則異常 if (null == this.primaryKeyIndex) { throw new IllegalStateException("使用@ExcelMerge注解必須指定主鍵"); } } }
1.5.6 定義接口
將自定義合并策略 ExcelMergeStrategy
通過(guò) registerWriteHandler
注冊(cè)上去
@RestController @RequestMapping("/excel") public class ExcelController { @GetMapping("/export/order") public void exportOrderExcel(HttpServletResponse response) { try { this.setExcelResponseProp(response, "訂單列表"); List<OrderDO> orderList = this.getOrderList(); List<OrderBO> exportData = this.convert(orderList); EasyExcel.write(response.getOutputStream()) .head(OrderBO.class) .registerWriteHandler(new ExcelMergeStrategy(OrderBO.class)) .excelType(ExcelTypeEnum.XLSX) .sheet("訂單列表") .doWrite(exportData); } catch (IOException e) { throw new RuntimeException(e); } } /** * 設(shè)置響應(yīng)結(jié)果 */ private void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(rawFileName, "UTF-8").replaceAll("\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); } }
到此這篇關(guān)于SpringBoot整合EasyExcel 3.x的文章就介紹到這了,更多相關(guān)SpringBoot整合EasyExcel 3.x內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合EasyExcel?3.x的完整示例
- SpringBoot整合EasyExcel實(shí)現(xiàn)Excel表格導(dǎo)出功能
- SpringBoot整合EasyExcel進(jìn)行大數(shù)據(jù)處理的方法詳解
- 使用VUE+SpringBoot+EasyExcel?整合導(dǎo)入導(dǎo)出數(shù)據(jù)的教程詳解
- SpringBoot整合EasyExcel實(shí)現(xiàn)導(dǎo)入導(dǎo)出數(shù)據(jù)
- SpringBoot整合EasyExcel的完整過(guò)程記錄
- SpringBoot整合EasyExcel實(shí)現(xiàn)文件導(dǎo)入導(dǎo)出
相關(guān)文章
SpringCloud項(xiàng)目中集成Sentinel問(wèn)題
在SpringCloud項(xiàng)目中集成Sentinel,可以實(shí)現(xiàn)流量控制、熔斷降級(jí)等功能,提升系統(tǒng)穩(wěn)定性和可用性,集成步驟包括添加Sentinel依賴、配置控制臺(tái)地址、啟動(dòng)控制臺(tái)、配置限流熔斷規(guī)則、使用注解和集成SpringCloudGateway,這有助于處理高并發(fā)場(chǎng)景,保護(hù)服務(wù)穩(wěn)定運(yùn)行2024-10-10IDEA中設(shè)置代碼自動(dòng)提示為Alt+/的具體做法
很多公司都強(qiáng)制性要求使用Intellij?IDEA,其實(shí)Intellij?IDEA也確實(shí)很好用,但是一下子從Eclipse跳轉(zhuǎn)到Intellij?IDEA轉(zhuǎn)也是需要一段時(shí)間的,為了迎合之前的習(xí)慣,就需要在Intellij?IDEA中改變一些設(shè)置,如代碼自動(dòng)生成,本文給大家分享設(shè)置方法,感興趣的朋友一起看看吧2023-01-01IntelliJ IDEA 2020常用配置設(shè)置大全(方便干活)
這篇文章主要介紹了IntelliJ IDEA 2020常用配置設(shè)置大全(方便干活),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02使用PageHelper插件實(shí)現(xiàn)Service層分頁(yè)
這篇文章主要為大家詳細(xì)介紹了使用PageHelper插件實(shí)現(xiàn)Service層分頁(yè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Java編程學(xué)習(xí)的幾個(gè)典型實(shí)例詳解
這篇文章主要給大家介紹了Java編程學(xué)習(xí)的幾個(gè)典型實(shí)例,其中包括模擬酒店房間管理系統(tǒng)、螺旋矩陣 例或者百雞問(wèn)題的變形等經(jīng)典實(shí)例,具體來(lái)一起看詳細(xì)內(nèi)容吧,需要的朋友可以參考學(xué)習(xí)。2017-02-02SpringBoot配置Redis自定義過(guò)期時(shí)間操作
這篇文章主要介紹了SpringBoot配置Redis自定義過(guò)期時(shí)間操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot-Mail工具實(shí)現(xiàn)郵箱驗(yàn)證碼登錄注冊(cè)功能
現(xiàn)在許多pc程序都有著使用郵箱驗(yàn)證碼實(shí)現(xiàn)登錄注冊(cè)的功能,那么我們應(yīng)該如何完成郵箱驗(yàn)證碼功能呢,我們可以使用springboot內(nèi)置的springboot-mail再結(jié)合redis來(lái)完成這個(gè)功能,感興趣的朋友跟隨小編一起看看吧2024-07-07