1. 程式人生 > >Excel匯入匯出工具

Excel匯入匯出工具

# Easy-POI

工具連結
https://github.com/programmeres/easy-poi
https://gitee.com/nw1992/easy-poi

Easy-POI是一款Excel匯入匯出解決方案組成的輕量級開源元件。

(如果喜歡或願意使用, 請star並且Watch本專案, 如果是企業使用, 請通過修改本檔案的企業列表告訴我企業名稱, 藉此給我們鼓勵及動力持續維護本專案。)

發現解決bug並已自測,pullRequest後,可以通過郵件告知我們([email protected]), 第一時間合併並且釋出最新版本
## 使用企業列表:

## 功能簡介
1. 瀏覽器匯出Excel檔案(支援單/多sheet)

2. 瀏覽器匯出Excel模板檔案  

3. 指定路徑生成Excel檔案(支援單/多sheet)

4. 返回Excel檔案(支援單/多sheet)的OutputStream, 一般用於將Excel檔案上傳到遠端, 例如FTP

5. 匯入Excel檔案(支援單/多sheet)

## 解決的問題
1.解決匯出大量資料造成的記憶體溢位問題(支援分頁查詢資料庫、採用poi官方推薦api(SXSSFWorkbook), 實現指定行數重新整理到磁碟)

2.解決匯入大量資料造成的記憶體溢位問題(分頁插入資料庫、採用poi官方推薦api(XSSF and SAX),採用SAX模式一行行讀取到記憶體當中去)

3.解決含有佔位符的空假行造成的讀空值問題

4.解決Long型別或者BigDecimal的精度不准問題

## 元件特色
1.匯入可以自定義解析成功或失敗的處理邏輯

2.匯出支援分頁查詢、全量查詢, 自定義每條資料的處理邏輯

3.內建快取,3萬條11列資料,第一次匯出2.2s左右、第二次匯出在1.4s左右;第一次匯入3.5s左右、第二次匯入2.5s左右

4.註解操作, 輕量且便捷

5.內建常用正則表示式類RegexConst(身份證號、手機號、金額、郵件)

6.適配單元格寬度(單元格內容最長不得超過20個漢字)

7.假如出現異常,Sheet、行、列位置也都一併列印

8.註解中的使用者自定義字串資訊以及Excel資訊已全部trim,不用擔心存在前後空格的風險

9.Excel樣式簡潔、大方、美觀

## 元件需知
匯入和匯出只支援尾綴為xlsx的Excel檔案、標註註解的屬性順序即Excel列的排列順序、時間轉化格式(dateFormat)預設為“yyyy-MM-dd HH:mm:ss“.

### 匯入
1.當匯入Excel, 讀取到空行, 則停止讀取當前Sheet的後面資料行

2.匯入Excel檔案, 單元格格式使用文字或者常規, 防止出現不可預測異常

3.匯入欄位型別支援:Date、Short(short)、Integer(int)、Double(double)、Long(long)、Float(float)、BigDecimal、String型別

4.匯入BigDecimal欄位精度預設為2, roundingMode預設為BigDecimal.ROUND_HALF_EVEN

5.第一行有效單元格內必須包含內容並且以第一行為依據, 匯入Excel檔案列數必須等於標註註解的屬性數量

6.Date型別欄位,Excel與時間轉化格式(dateFormat)相比,格式要保持一致(反例:2018/12/31和“yyyy-MM-dd“)並且長度要一致或更長(反例:"2018-12-31"和yyyy-MM-dd HH:mm:ss"),否則SimpleDateFormat將解析失敗,報 “Unparseable date:”

### 匯出
1.匯出BigDecimal欄位預設不進行精度格式化

2.分頁查詢預設從第一頁開始, 每頁3000條

3.Excel每超過2000條資料, 將記憶體中的資料重新整理到磁碟當中去

4.使用分Sheet匯出方法, 每8萬行資料分Sheet

5.當使用(exportResponse、exportStream、generateExcelStream)方法時, 當單個Sheet超過100萬條則會分Sheet

6.標註屬性型別要與資料庫型別保持一致


## 擴充套件
繼承EasyPoi類, 可以使用子類構造器覆蓋以下預設引數
```java
    //Excel自動重新整理到磁碟的數量
    public static final int DEFAULT_ROW_ACCESS_WINDOW_SIZE = 2000;
    //分頁條數
    public static final int DEFAULT_PAGE_SIZE = 3000;
    //分Sheet條數
    public static final int DEFAULT_RECORD_COUNT_PEER_SHEET = 80000;
```
## 版本
當前為1.0.0版本, 2.0.0版正在開發當中

## 使用手冊
1.引入Maven依賴

2.將需要匯出或者匯入的實體屬性上標註@ExportField或@ImportField註解

3.直接呼叫匯出或匯入API即可

### POM.xml 

```xml
<dependency>
   <groupId>io.github.magic-core</groupId>
   <artifactId>easy-poi</artifactId>
   <version>1.0</version>
</dependency>
```
### @ExportField

```java
/**
* 匯出註解功能介紹
*/
public @interface ExportField {

    /**
     * excel列名稱
     */
    String columnName();

    /**
     * 預設單元格值
     */
    String defaultCellValue() default "";

    /**
     * 日期格式 預設 yyyy-MM-dd HH:mm:ss
     */
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";

    /**
     * BigDecimal精度 預設:-1(預設不開啟BigDecimal格式化)
     */
    int scale() default -1;

    /**
     * BigDecimal 舍入規則 預設:BigDecimal.ROUND_HALF_EVEN
     */
    int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
}
```
```java
/**
* 匯出註解Demo
*/
public class ExportFielddemo {
@ExportField(columnName = "ID", defaultCellValue = "1")
    private Integer id;

@ExportField(columnName = "姓名", defaultCellValue = "張三")
    private String name;
    
@ExportField(columnName = "收入金額", defaultCellValue = "100", scale = 2, roundingMode=BigDecimal.ROUND_HALF_EVEN)
    private BigDecimal money;

@ExportField(columnName = "建立時間", dateFormat="yyyy-MM-dd", defaultCellValue = "2019-01-01")
    private Date createTime;
}
```

### @ImportField

```java
/**
* 匯入註解功能介紹
*/
public @interface ImportField {

    /**
     * @return 是否必填
     */
    boolean required() default false;

    /**
     * 日期格式 預設 yyyy-MM-dd HH:mm:ss
     */
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";

    /**
     * 正則表示式校驗
     */
    String regex() default "";

    /**
     * 正則表示式校驗失敗返回的錯誤資訊, regex配置後生效
     */
    String regexMessage() default "正則表示式驗證失敗";

    /**
     * BigDecimal精度 預設:2
     */
    int scale() default 2;

    /**
     * BigDecimal 舍入規則 預設:BigDecimal.ROUND_HALF_EVEN
     */
    int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
}
```
```java
/**
* 匯入註解Demo
*/
public class ImportField {
@ImportField(required = true)
    private Integer id;

@ImportField(regex = IDCARD_REGEX, regexMessage="身份證校驗失敗")
    private String idCard;
    
@ImportField(scale = 2, roundingMode=BigDecimal.ROUND_HALF_EVEN)
    private BigDecimal money;

@ImportField(dateFormat="yyyy-MM-dd")
    private Date createTime;
}
```
### 匯出Demo
```java
/**
 * 匯出Demo
 */
public class ExportDemo {
    /**
     * 瀏覽器匯出Excel
     *
     * @param httpServletResponse
     */
    public void exportResponse(HttpServletResponse httpServletResponse) {
        ParamEntity queryQaram = new ParamEntity();
        EasyPoi.ExportBuilder(httpServletResponse, "Excel檔名", AnnotationEntity.class).exportResponse(queryQaram,
                new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 瀏覽器多sheet匯出Excel
     *
     * @param httpServletResponse
     */
    public void exportMultiSheetResponse(HttpServletResponse httpServletResponse) {
        ParamEntity queryQaram = new ParamEntity();
        EasyPoi.ExportBuilder(httpServletResponse, "Excel檔名", AnnotationEntity.class).exportMultiSheetStream(queryQaram,
                new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<ResultEntity>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 匯出Excel到指定路徑
     */
    public void exportStream() throws FileNotFoundException {
        ParamEntity queryQaram = new ParamEntity();
        EasyPoi.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel檔案.xlsx")), "Sheet名", AnnotationEntity.class)
                .exportStream(queryQaram, new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 匯出多sheet Excel到指定路徑
     */
    @RequestMapping(value = "exportResponse")
    public void exportMultiSheetStream() throws FileNotFoundException {
        ParamEntity queryQaram = new ParamEntity();
        EasyPoi.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel檔案.xlsx")), "Sheet名", AnnotationEntity.class)
                .exportMultiSheetStream(queryQaram, new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 生成Excel OutputStream物件
     */
    public void generateExcelStream() throws FileNotFoundException {
        ParamEntity queryQaram = new ParamEntity();
        OutputStream outputStream = EasyPoi.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel檔案.xlsx")), "Sheet名", AnnotationEntity.class)
                .generateExcelStream(queryQaram, new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 生成多Sheet Excel OutputStream物件
     */
    public void generateMultiSheetExcelStream() throws FileNotFoundException {
        ParamEntity queryQaram = new ParamEntity();
        OutputStream outputStream = EasyPoi.ExportBuilder(new FileOutputStream(new File("C:\\Users\\Excel檔案.xlsx")), "Sheet名", AnnotationEntity.class)
                .generateMultiSheetExcelStream(queryQaram, new ExportFunction<ParamEntity, ResultEntity>() {
                    /**
                     * @param queryQaram 查詢條件物件
                     * @param pageNum    當前頁數,從1開始
                     * @param pageSize   每頁條數,預設3000
                     * @return
                     */
                    @Override
                    public List<ResultEntity> pageQuery(ParamEntity queryQaram, int pageNum, int pageSize) {

                        //分頁查詢操作

                        return new ArrayList<>();
                    }

                    /**
                     * 將查詢出來的每條資料進行轉換
                     *
                     * @param o
                     */
                    @Override
                    public void convert(ResultEntity o) {
                        //轉換操作
                    }
                });
    }

    /**
     * 匯出Excel模板
     */
    public void exportTemplate(HttpServletResponse httpServletResponse) {
        EasyPoi.ExportBuilder(httpServletResponse, "Excel模板名稱", AnnotationEntity.class).exportTemplate();
    }
}
```
### 匯入Demo
```java
/**
 * 匯入Demo
 */
public class ImportDemo {
    public void importExcel() throws IOException {
        EasyPoi.ImportBuilder(new FileInputStream(new File("C:\\Users\\匯入Excel檔案.xlsx")),  AnnotationEntity.class)
                .importExcel(new ExcelImportFunction<ResultEntity>() {

                    /**
                     * @param sheetIndex 當前執行的Sheet的索引, 從1開始
                     * @param rowIndex 當前執行的行數, 從1開始
                     * @param resultEntity Excel行資料的實體
                     */
                    @Override
                    public void onProcess(int sheetIndex,  int rowIndex,  ResultEntity resultEntity) {
                        //對每條資料自定義校驗以及操作
                        //分頁插入:當讀取行數到達使用者自定義條數執行插入資料庫操作
                    }

                    /**
                     * @param errorEntity 錯誤資訊實體
                     */
                    @Override
                    public void onError(ErrorEntity errorEntity) {
                        //操作每條資料非空和正則校驗後的錯誤資訊
                    }
                });
    }
}
```