最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot整合EasyExcel?3.x的完整示例

 更新時(shí)間:2023年07月07日 08:32:07   作者:愛(ài)吃牛肉的大老虎  
EasyExcel 是一個(gè)基于 Java 的、快速、簡(jiǎn)潔、解決大文件內(nèi)存溢出的 Excel 處理工具,它能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成 Excel 的讀、寫(xiě)等功能,這篇文章主要介紹了SpringBoot整合EasyExcel3.x的過(guò)程,需要的朋友可以參考下

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í)體類

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í)體類,用于封裝用戶信息:

@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;
}

上面代碼中類屬性上使用了EasyExcel核心注解:

  • @ExcelProperty:核心注解,value屬性可用來(lái)設(shè)置表頭名稱,converter屬性可以用來(lái)設(shè)置類型轉(zhuǎn)換器;
  • @ColumnWidth:用于設(shè)置表格列的寬度;
  • @DateTimeFormat:用于設(shè)置日期轉(zhuǎn)換格式;
  • @NumberFormat:用于設(shè)置數(shù)字轉(zhuǎn)換格式。

1.3.2 自定義轉(zhuǎn)換器

EasyExcel中,如果想實(shí)現(xiàn)枚舉類型到字符串類型轉(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í)體類

在需要合并單元格的屬性上設(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ù)類型
     */
    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);
        // 將本行和上一行是同一類型的數(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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 利用棧來(lái)反轉(zhuǎn)鏈表和排序的操作

    Java 利用棧來(lái)反轉(zhuǎn)鏈表和排序的操作

    這篇文章主要介紹了Java 利用棧來(lái)反轉(zhuǎn)鏈表和排序的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • spring-data-jpa使用自定義repository來(lái)實(shí)現(xiàn)原生sql

    spring-data-jpa使用自定義repository來(lái)實(shí)現(xiàn)原生sql

    這篇文章主要介紹了在spring-data-jpa中使用自定義repository來(lái)實(shí)現(xiàn)原生sql,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • SpringBoot請(qǐng)求參數(shù)傳遞與接收示例詳解

    SpringBoot請(qǐng)求參數(shù)傳遞與接收示例詳解

    本文給大家介紹SpringBoot請(qǐng)求參數(shù)傳遞與接收示例詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-08-08
  • mybatis-plus中的Enum用法實(shí)例

    mybatis-plus中的Enum用法實(shí)例

    本文主要介紹了mybatis-plus中的Enum用法實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 詳解Java編寫(xiě)算法時(shí)如何加快讀寫(xiě)數(shù)據(jù)速度

    詳解Java編寫(xiě)算法時(shí)如何加快讀寫(xiě)數(shù)據(jù)速度

    這篇文章主要為大家詳細(xì)介紹了Java在編寫(xiě)算法時(shí)如何加快讀寫(xiě)數(shù)據(jù)速度,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Java中的ReentrantLock使用解析

    Java中的ReentrantLock使用解析

    這篇文章主要介紹了Java中的ReentrantLock使用解析,ReentrandLock即可重入鎖,可重入鎖解決的是重入鎖定的問(wèn)題,重入鎖定指的是當(dāng)一個(gè)線程執(zhí)行邏輯時(shí),需要兩次獲取鎖,而該鎖不可重入就會(huì)導(dǎo)致內(nèi)部嵌套無(wú)法獲取鎖導(dǎo)致Reentrance Lockout發(fā)生,需要的朋友可以參考下
    2023-11-11
  • Java接口定義與實(shí)現(xiàn)方法分析

    Java接口定義與實(shí)現(xiàn)方法分析

    這篇文章主要介紹了Java接口定義與實(shí)現(xiàn)方法,簡(jiǎn)單說(shuō)明了接口的概念、功能,并結(jié)合實(shí)例形式分析了接口的相關(guān)定義與使用技巧,需要的朋友可以參考下
    2017-11-11
  • spring boot+mybatis 多數(shù)據(jù)源切換(實(shí)例講解)

    spring boot+mybatis 多數(shù)據(jù)源切換(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇spring boot+mybatis 多數(shù)據(jù)源切換(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • Spring Boot 2.0 設(shè)置網(wǎng)站默認(rèn)首頁(yè)的實(shí)現(xiàn)代碼

    Spring Boot 2.0 設(shè)置網(wǎng)站默認(rèn)首頁(yè)的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Spring Boot 2.0 設(shè)置網(wǎng)站默認(rèn)首頁(yè)的實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2018-04-04
  • IntelliJ IDEA 2023.2正式發(fā)布新UI和Profiler轉(zhuǎn)正(最新推薦)

    IntelliJ IDEA 2023.2正式發(fā)布新UI和Profiler轉(zhuǎn)正(最新推薦)

    北京時(shí)間2023年7月26日,IntelliJ IDEA 2023.2正式發(fā)布,IntelliJ IDEA 2023.2 引入 AI Assistant(AI助手),通過(guò)一組由 AI 提供支持的功能助力開(kāi)發(fā),今天給大家分享IntelliJ IDEA 2023.2正式發(fā)布新UI和Profiler轉(zhuǎn)正,感興趣的朋友一起看看吧
    2023-10-10

最新評(píng)論

延川县| 五原县| 乌审旗| 祁东县| 南溪县| 景东| 外汇| 荔波县| 柳江县| 宿松县| 西乡县| 图木舒克市| 江阴市| 双柏县| 宜丰县| 盐池县| 石城县| 肃宁县| 淅川县| 江达县| 中卫市| 邓州市| 海南省| 治多县| 杭锦后旗| 忻州市| 平顶山市| 白河县| 喀什市| 阿拉善左旗| 宁强县| 贵定县| 宁国市| 凤台县| 深州市| 吉木乃县| 安溪县| 克东县| 桐乡市| 安图县| 桃园市|