亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring?Boot項目怎么實現Excel導入與導出功能

發布時間:2022-06-13 14:11:25 來源:億速云 閱讀:248 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Spring Boot項目怎么實現Excel導入與導出功能”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Spring Boot項目怎么實現Excel導入與導出功能”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    背景

    Excel 導入與導出是項目中經常用到的功能,在 Java 中常用 poi 實現 Excel 的導入與導出。由于 poi 占用內存較大,在高并發下很容易發生 OOM 或者頻繁 fullgc,阿里基于 poi 開源了 EasyExcel 項目。

    除了節約內存,EasyExcel 還簡化了 API,通過注解映射 Excel 單元格與對象字段之間的關系,簡單的幾行代碼就能搞定復雜的導入導出功能了。

    EasyExcel 問題

    看似一切美好,不過經常做 Excel 導入與導出就會發現,EasyExcel 還是沒那么完美的。

    首先,導入與導出 Excel 本質是上將 Excel 文件內容與 Java 對象之間做一個映射,EasyExcel 做的只是在這兩者之間轉換。如果項目中的 Excel 導入與導出功能比較多,會產生大量的樣板式代碼,使用體驗類似于 JDBC。

    另外,導入往往還伴隨著校驗,這是 EasyExcel 沒有支持的功能。如果需要校驗,要么寫代碼手動判斷,要么調用 Java Validation 規范 定義的 API 判斷,這又會產生大量樣板式代碼。

    而且,當前 spring boot 已經成了必備的 Java 開發框架,easyexcel 也沒有進行整合。

    分析與解決

    導入與導出通常發生在 Web 環境,對于 Spring MVC 來說,可以將請求信息轉換為任意類型的 contoller 方法參數,將 controller 方法返回值轉換為客戶端支持的內容。

    如果能夠使用自定義的 controller 方法參數接收 Excel 文件內容,將 controller 方法返回值轉換為 Excel 文件響應,可以直接消除 Excel 導入與導出時的樣板式代碼。

    另外在將請求內容轉換為 controller 方法參數時還可以加入自定義的校驗邏輯。

    由于 Excel 導入與導出樣板式代碼、校驗問題與具體的業務邏輯無關,可以單獨抽象出來,我這里在 EasyExcel 的基礎上封裝了一個 easyexcel-spring-boot-starter 的項目,大大降低了 EasyExcel 上手的門檻,對用戶來說只需要使用 EasyExcel 定義的注解提供映射關系就可以了,適用于簡單場景的導入導出。

    Spring Boot Excel 導入與導出

    依賴引入

    首先需要引入依賴,坐標如下。

    <dependency>
        <groupId>com.zzuhkp</groupId>
        <artifactId>easyexcel-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    不過很不幸的是目前還沒傳至中央倉庫,需要的小伙伴可自行上傳到私有倉庫或直接把代碼嵌入自己的項目。

    Excel 導入

    首先看下要導入的 Excel 內容吧。

    Spring?Boot項目怎么實現Excel導入與導出功能

    為了接收 Excel 文件內容,我們需要定義一個對應的 Model 類。

    @Data
    public class DemoData {
        @ExcelProperty(index = 0)
        private Integer integer;
    
        @ExcelProperty(index = 1)
        private String string;
    
        @ExcelProperty(index = 2)
        private Date date;
    }
    基本導入功能

    然后使用 List<T> 參數接收即可。

    @PostMapping("/list/obj")
    public List<DemoData> listObj(@ExcelParam List<DemoData> list) {
        return list;
    }

    注意參數前添加了 @ExcelParam 注解,用來標識 Excel 文件參數。這樣,一個導入功能實現了,是不是很簡單呢?

    默認情況下接收名稱為 file 的表單字段作為 Excel 文件,如果不滿足還可以修改。

    @ExcelParam(value = "file", required = true)
    進階導入功能

    有時候,我們可能比較關心對象對應 Excel 的元數據,例如這個對象是第幾行記錄產生的,這個對象的字段對應 Excel 第幾列,這個時候我們可以使用 ReadRows<T> 參數接收 Excel。

    @PostMapping("/list/rows")
    public ReadRows<DemoData> readRows(@ExcelParam ReadRows<DemoData> readRows) {
        return readRows;
    }

    ReadRows 使用兩個字段記錄行映射關系與列映射關系。

    public class ReadRows<T> {
    
        private ExcelReadHeadProperty excelReadHeadProperty;
    
        private List<ReadRow<T>> rows;
    }

    ExcelReadHeadProperty 是 EasyExcel 自帶的類,表示列映射關系的元數據。ReadRow 是框架自定義的類,表示行映射關系的元數據。

    看下 ReadRow 定義吧。

    public class ReadRow<T> {
    
        // 行索引,從 0 開始
        private final Integer rowIndex;
    
        // 行記錄對應對象
        private final T data;
    }

    使用 ExcelReadHeadProperty 獲取字段對應列索引的示例代碼如下。

    // 對象字段名稱 -> 從 0 開始的列索引
    Map<String, Integer> fieldColumnIndexMap = readRows.getExcelReadHeadProperty().getHeadMap().values()
            .stream().collect(Collectors.toMap(Head::getFieldName, Head::getColumnIndex));

    Excel 導出

    這里對 Excel 的導出進行了簡單的支持。將 List<T> 定義為 controller 方法返回值即可。

    @ExcelResponse
    @GetMapping("/list/download")
    public List<DemoData> downloadList() {
        return Arrays.asList(new DemoData(1, "hello", new Date()), new DemoData(2, "excel", new Date()));
    }

    需要注意的是使用 @ExcelResponse 注解表示響應內容為 Excel 文件。默認情況,下載的文件名稱為 default.xlxs,寫入到名稱為 Sheet1 的工作表中。如果不滿足需求可以修改。

    @ExcelResponse(fileName = "測試文件", sheetName = "工作表1")

    Excel 導入參數校驗

    參數校驗是 Excel 導入常用的功能,這里進行了強有力的支持,使用體驗如原生 spring boot 校驗般順滑。

    開啟校驗

    與 spring boot 原生使用方式一樣,將 @Validated@Valid 注解添加到 @ExcelParam 參數上即可。

    @PostMapping("/list/obj")
    public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list) {
        return list;
    }

    校驗規則定義

    Bean Validation 定義校驗規則

    默認情況下框架使用 JSR-303 Bean Validation 規范定義的校驗注解校驗,需要手動引入 spring-boot-starter-validation,可通過設置環境變量 easyexcel.validator.default.enable=false 關閉。

    @Data
    public class DemoData {
        @NotNull(message = "參數不能為空")
        private Integer integer;
    
        private String string;
    
        private Date date;
    }

    另外還可以自定義注解對對象校驗。

    ... 省略其他元注解
    @Constraint(validatedBy = {DemoDataValid.DemoDataValidator.class})
    public @interface DemoDataValid {
    		... 省略注解屬性
    		
        class DemoDataValidator implements ConstraintValidator<DemoDataValid, DemoData> {
    
            @Override
            public boolean isValid(DemoData value, ConstraintValidatorContext context) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("測試對象校驗").addConstraintViolation();
                return false;
            }
        }
    }
    @DemoDataValid
    public class DemoData {
        ... 省略屬性
    }
    ExcelValidator 接口定義校驗規則

    Bean Validation 注解只能校驗單個字段或對象,如果需要對所有的對象進行校驗,可以實現框架定義的 ExcelValidator 接口,然后將實現定義為 Spring Bean。

    這個接口定義如下。

    public interface ExcelValidator<T> {
        ExcelValidErrors validate(ReadRows<T> readRows);
    }

    ExcelValidErrors 用于接收校驗的錯誤信息,分別使用接口 ExcelValidObjectErrorExcelValidFieldError 接口定義行錯誤信息和單元格錯誤信息。

    public class ExcelValidErrors {
    	// 行錯誤信息或單元格錯誤信息列表
        private final List<ExcelValidObjectError> errors;
    }
    
    public interface ExcelValidObjectError {
        // 獲取行號,從 1 開始
        Integer getRow();
    
        // 獲取錯誤消息
        String getMessage();
    }
    
    public interface ExcelValidFieldError extends ExcelValidObjectError {
        // 獲取列,從 1 開始
        Integer getColumn();
    }

    例如,如果需要對所有的 DemoData 校驗 integer 字段的值不能重復,可以使用如下的代碼。

    @Component
    public class CustomExcelValidator implements ExcelValidator<DemoData> {
        @Override
        public ExcelValidErrors validate(ReadRows<DemoData> readRows) {
            ExcelValidErrors errors = new ExcelValidErrors();
    
            Map<Integer, List<ReadRow<DemoData>>> group = readRows.getRows().stream()
                    .collect(Collectors.groupingBy(item -> item.getData().getInteger()));
    
            for (Map.Entry<Integer, List<ReadRow<DemoData>>> entry : group.entrySet()) {
                if (entry.getValue().size() > 1) {
                    for (ReadRow<DemoData> readRow : entry.getValue()) {
                        errors.addError(new DefaultExcelObjectError(readRow.getRowIndex() + 1, "參數重復"));
                    }
                }
            }
            return errors;
        }
    }

    校驗結果接收

    與 Spring MVC 設計類似,這里也提供了兩種接收校驗結果的方式。

    異常捕獲接收校驗結果

    開啟校驗后,如果校驗結果中包含錯誤,會將錯誤信息封裝到 ExcelValidException,并拋出異常,可以通過全局異常捕獲的方式收集錯誤信息。

    @RestControllerAdvice
    public class GlobalExceptionControllerAdvice {
        @ExceptionHandler(ExcelValidException.class)
        public String handleException(ExcelValidException e) {
            ExcelValidErrors errors = e.getErrors();
            return JSON.toJSONString(errors);
        }
    }
    controller 方法參數接收校驗結果

    如果不想通過異常捕獲的方式接收校驗的錯誤信息,還可以將錯誤信息添加到 @ExcelParam 參數的后面,示例代碼如下。

    @PostMapping("/list/obj")
    public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list, ExcelValidErrors errors) {
        if (errors.hasErrors()) {
            String messages = errors.getAllErrors().stream().map(ExcelValidObjectError::getMessage).collect(Collectors.joining(" | "));
            throw new RuntimeException("發現異常:" + messages);
        }
        return list;
    }

    讀到這里,這篇“Spring Boot項目怎么實現Excel導入與導出功能”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    屏边| 阿巴嘎旗| 治多县| 宜章县| 宁明县| 洛扎县| 迭部县| 德庆县| 嫩江县| 泸水县| 红安县| 高阳县| 修文县| 台安县| 泌阳县| 玉门市| 虎林市| 兴国县| 横山县| 尚义县| 伊春市| 文登市| 即墨市| 望谟县| 石林| 万山特区| 岳普湖县| 乐东| 海晏县| 谷城县| 德兴市| 微山县| 东阿县| 平顺县| 从江县| 丽水市| 巴彦县| 榆中县| 喜德县| 徐州市| 乌鲁木齐市|