1. 程式人生 > >Apache POI實現Excel檔案匯出

Apache POI實現Excel檔案匯出

文章轉載地址:

https://blog.csdn.net/qq_17214237/article/details/78345246

最近開發excel匯入匯出功能,使用的是Apache的POI技術

POI提供了很多對Microsoft Office的功能,本文僅僅講解POI的Excel匯出功能

版本如下:

 <dependency>    
<groupId>org.apache.poi</groupId>    
<artifactId>poi-ooxml-schemas</artifactId>     
<version>3.10-FINAL</version>
</dependency>  

<dependency>    
<groupId>org.apache.poi</groupId>    
<artifactId>poi-ooxml</artifactId>     
<version>3.10-FINAL</version>
</dependency>  

<dependency>    
<groupId>org.apache.poi</groupId>    
<artifactId>poi</artifactId>     
<version>3.10-FINAL</version>
</dependency>

幾個關鍵物件: 
HSSFWorkbook類就是一個excel檔案 
這裡寫圖片描述

一個excel檔案有很多頁,HSSFSheet就是一個頁

這裡寫圖片描述

一頁有很多行,HSSFRow代表一行 
這裡寫圖片描述

一行有很多格,HSSFCell代表一個單元格 
這裡寫圖片描述

前輩大牛充分發揮了Java的抽象,封裝,繼承,多型,寫出瞭如此優雅的程式碼….爾等渣渣只能膜拜

我的想法是利用反射獲取JavaBean的欄位和值,寫一個通用的Excel工具類,並可以在客戶端選擇儲存路徑,程式碼分為幾個部分講解: 
第一部分:

 //反射,獲取類物件
        Object obj = list.get(0);
        Class clazz = obj.getClass();

        //建立excel工作簿
        HSSFWorkbook wb = new HSSFWorkbook();
        //建立頁 ,指定頁的名字就是表名,excel有很多頁
        HSSFSheet sheet = wb.createSheet(clazz.getSimpleName());
        //設定單元格樣式,居中顯示
        HSSFCellStyle style = wb.createCellStyle();
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);

因為傳入的資料集合肯定是相同物件,所以獲取第一個物件的類物件,並建立好excel檔案,與第一頁,頁的名字就用表名來命名,並指定單元格居中顯示。

第二部分:

  //獲取欄位物件
        Field[] fields = clazz.getDeclaredFields();

        //欄位名陣列,只需要標識了註解的欄位
        List<String> names = new ArrayList<String>();
        for(int i=0,len=fields.length;i<len;i++){
            //獲取欄位是否有匯出標識註解
            ExcelField field = fields[i].getAnnotation(ExcelField.class);
            if(field!=null) {
                //如果指定了列名,就使用列名,不然使用欄位名
                if(field.title()!=null){
                    names.add(field.title());
                }else{
                    names.add(fields[i].getName());
                }
            }
        }

        //建立第一行
        HSSFRow row = sheet.createRow(0);
        for(int i=0,len=names.size();i<len;i++){
            //建立單元格
            HSSFCell cell = row.createCell(i);
            //指定值
            cell.setCellValue(names.get(i));
            //指定寬度
            sheet.setColumnWidth(i,5000);
            //設定樣式
            cell.setCellStyle(style);
            //寬度自適應
           // sheet.autoSizeColumn(i);
        }

因為一個JavaBean有很多的屬性,大部分情況下不會對全部的欄位都需要匯出,如何只匯出指定物件的欄位與值呢?作者在這裡採用了註解來標識,利用Java反射獲取物件的全部欄位,但是卻只對標識了註解的欄位進行excel匯出,還可以自己確定列名 

註解程式碼:

/**
 * Created by oldfish on 2017-10-20.
 * 匯出註解類,用於標識欄位是否需要匯出
 */
@Target({ElementType.FIELD})//只作用在欄位上
@Retention(RetentionPolicy.RUNTIME)//執行時有效
public @interface ExcelField {
    //匯出欄位在excel的名字
    String title();
}

註解使用程式碼:

@ExcelField(title = "編號")
    private Integer id; // 編號
    @ExcelField(title = "使用者IP")
    private String userIp; // 使用者IP
    @ExcelField(title = "評論內容")
    private String content; // 評論內容
    private Blog blog; // 被評論的部落格
    @ExcelField(title = "評論日期")
    private Date commentDate; // 評論日期
    private Integer blogId;
    @ExcelField(title = "稽核狀態")
    private Integer state; // 稽核狀態  0 待稽核 1 稽核通過 2 稽核未通過

    private String commentDateStart; //開始日期 用於條件查詢

    private String commentDateEnd;//結束日期 用於條件查詢

獲取了需要的欄位名,並用陣列儲存好,就需要建立好第一行用來顯示欄位名,迴圈陣列長度,確定有多少列,指定好列名(就是欄位名),寬度與樣式等,這裡有個小問題,如果指定了列名的寬度就不要寬度自適應,兩者只會產生一個效果 
這裡寫圖片描述

第三部分:

 for(int j=0,l=list.size();j<l;j++){
            Object temp = list.get(j);
            Class c = temp.getClass();
            Field[] fields1 = c.getDeclaredFields();
            //一個物件建立一個行,從第二行開始
            HSSFRow row1 = sheet.createRow(j+1);

            //用於作序號,因為迴圈的序號可能會發生有些屬性不注入,序號就會斷層
            //需要另外用變數來維持序號
            int k = 0;
            for(int i=0,len=fields1.length;i<len;i++){

                //只有 有標識註解的屬性才匯出
                ExcelField excelField = fields1[i].getAnnotation(ExcelField.class);
                if(excelField!=null) {

                    //根據指定的屬性建立指定的單元格
                    HSSFCell cell = row1.createCell(k);

                    //設定值,因為不能直接放置Object,所以需要確定型別再轉換
                    try {
                        fields1[i].setAccessible(true);
                        Object value = fields1[i].get(temp);
                        if (value instanceof String) {
                            cell.setCellValue(value.toString());
                        } else if (value instanceof Date) {
                            Date date = (Date) value;
                            cell.setCellValue(DateUtil.formatDate(date, "yyyy-MM-dd"));
                        } else if (value instanceof Integer) {
                            cell.setCellValue((Integer) value);
                        } else if (value instanceof Double) {
                            cell.setCellValue((Double) value);
                        } else if (value instanceof Boolean) {
                            cell.setCellValue((Boolean) value);
                        } else if (value instanceof Float) {
                            cell.setCellValue((Float) value);
                        } else if (value instanceof Short) {
                            cell.setCellValue((Short) value);
                        } else if (value instanceof Character) {
                            cell.setCellValue((Character) value);
                        }
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException("型別轉換錯誤");
                    }
                    //設定樣式
                    cell.setCellStyle(style);

                    k++;
                }
            }
        }

然後就需要迴圈資料集合開始匯出資料了,這裡有個需要注意的地方就是第一行已經被列名佔用了,所以建立行肯定是從第二行開始,同樣需要判斷欄位有沒有註解,然後反射獲取值。因為單元格設定值的方法不能是Object,很蛋疼,所以需要判斷型別,然後轉換再賦值,如果有更好的辦法,歡迎指點作者與廣大讀者。

還有一個坑的地方就是迴圈欄位時,會發生某個欄位不需要匯出,然後順序就會發生斷層,建立單元格的時候肯定就不能用這個迴圈變數來建立,需要另外的迴圈變數,程式碼中已指出,如果有更簡單,更效率的辦法,我是很歡迎大家來指點的啦

第四部分:

response.setContentType("application/vnd.ms-excel;charset=utf-8");
        //檔名使用uuid,避免重複
        response.setHeader("Content-Disposition", "attachment;filename="
                + UUID.randomUUID() + ".xls");
        OutputStream ouputStream = null;
        try {
            ouputStream = response.getOutputStream();
            wb.write(ouputStream);
            ouputStream.flush();
            ouputStream.close();
        } catch (IOException e) {
            throw new RuntimeException("IO異常");
        }

最後用response物件設定Http協議並指定編碼,用uuid指定檔名,防止重複,用輸出流輸出到客戶端。一個基本的excel匯出通用工具類就完成了….