筆記(一)-EXCEL格式導出從數據庫中查詢的數據-上傳至服務器


以Excel格式導出查詢出的數據是日常開發中常用到的功能。本人通過閱讀若依開源項目源碼,總結記錄了若依是如何實現該功能的。

以Excel格式導出查詢出的數據運用到了自定義注解、反射等基礎知識,以及POI。具體步驟如下:

一、導入POI的jar包

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.0.0</version>
</dependency>
或
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.0.0</version>
</dependency>

二、POI的使用

POI的基本使用包括:創建工作薄、創建工作表、創建行、創建列、填充數據、設置樣式

  1. 創建工作薄
SXSSFWorkbook wb = new SXSSFWorkbook(100);//100指的是最多可以容納100個數據
  1. 創建工作表
Sheet sheet = wb.createSheet("sheetName");//參數是工作表名稱
//也可先創建工作表,再設置名稱
Sheet sheet = wb.createSheet();
Sheet sheet = wb.setSheetName(1, "sheetName");//第一個工作表的名字
  1. 創建行
Row row = sheet.createRow(0);//參數是第幾行,這里是第0行,從第0行開始
  1. 創建列
Cell cell = row.createCell(0);//參數是第幾列,這里是第0列,從第0列開始

  1. 填充數據
cell.setCellValue("name"); //參數是要填充的數據
  1. 設置樣式
//創建樣式
CellStyle style = wb.createCellStyle();
//1設置對齊方式
style.setAlignment(HorizontalAlignment.CENTER);//水平居中對齊
style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中對齊

//2單元格邊框樣式
style.setBorderLeft(BorderStyle.THIN);//細線
style.setLeftBorderColor(IndexedColors.BLUE.getIndex());//藍色
style.setBorderRight(BorderStyle.THIN);//破折號-點
style.setLeftBorderColor(IndexedColors.BLUE.getIndex());
style.setBorderTop(BorderStyle.THIN);//
style.setTopBorderColor(IndexedColors.BLUE.getIndex());
style.setBorderBottom(BorderStyle.THIN);//虛線
style.setBottomBorderColor(IndexedColors.BLUE.getIndex());

//3設置數據字體大小
Font dataFont = wb.createFont();
dataFont.setFontHeightInPoints((short) 10);
dataFont.setColor(IndexedColors.BLACK.getIndex());//設置字體顏色
dataFont.setFontName("Arial");//設置字體
style.setFont(dataFont);
//添加屬性
cell.setCellStyle(style);

三、編寫自定義注解Excel

在導出數據的時候,如果我們想導出自己想要的數據,注解就起了關鍵作用。我們可以在實體類中將自己想要導出的數據的字段上加上此注解,然后再利用反射獲取這些加了注解的字段及其值,將其填入excel表格中即可。如下面的的例子所示

/**
 * Excel注解
 */
@Target({ElementType.FIELD}) //只作用在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
    /**
     * excel表格列頭名字
     * @return
     */
    String name() default "";

    /**
     * 文字后綴  如90 -> 90%
     * @return
     */
    String suffix() default "";

    /**
     * excel表每列的高度 默認為 14 單位為字符
     * @return
     */
    double height() default 14;

    /**
     * excel表每列的寬度 默認為 16 單位為字符
     * @return
     */
    double weight() default 16;

    /**
     * 填入表格中的數據類型 0 String, 1 Number, 2 IMAGE
     * @return
     */
    ColumnType columnType() default ColumnType.STRING;

    /**
     * 枚舉數據類型
     */
    public enum ColumnType{
        NUMBER(0),
        STRING(1),
        IMAGE(2);

        private final int value;

        ColumnType(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }
}

例子中注解的屬性可以根據自己的需求添加。就算注解中一個屬性都沒有,照樣可以將所想要的屬性填充到Excel表格中,只是某些某些樣式及類型轉換方面比較麻煩。

String name() default ""; 這是設置列表頭的名稱,像實體類中“姓名”,“年齡”。。。都可以通過注解設置,在后面用反射獲取即可。

四、使用注解

使用注解,在實體類的字段上加上即可,如下

@Excel(name = "id", columnType = Excel.ColumnType.NUMBER)
private Integer id; //ID
@Excel(name = "姓名")
private String name; //姓名
@Excel(name = "性別")
private String gender; //性別
@Excel(name = "年齡")
private Integer age; //年齡
@Excel(name = "地址")
private String address; //地址
private String qq; //qq

五、編寫Excel工具類

1、編寫初始化函數

這個函數的作用是利用反射獲取實體類中加了Excel注解的字段和該注解,並保存下來。創建一個新的工作薄

public void init(List<T> dataList, String sheetName) {
    if (dataList == null) {
        dataList = new ArrayList<>();
    }
    this.dataList = dataList;
    this.sheetName = sheetName;
    //獲取所有字段,表列頭名稱
    createField();
    //創建一個工作薄
    createWorkBook();
}
/**
* 利用反射,獲取所有字段,表列頭名稱
*/
public void createField() {
    this.fields = new ArrayList<>();
    Field[] tempFields = clazz.getDeclaredFields();//獲取所有字段,無論權限

    //遍歷tempField,尋找帶有Excel注解的字段,並將field和注解對象裝進fields
    for (Field field : tempFields) {
        //判斷該字段上是否有Excel注解
        if (field.isAnnotationPresent(Excel.class)) {
            Excel excel = field.getAnnotation(Excel.class);
            this.fields.add(new Object[]{field, excel});
        }
    }
}

/**
* 創建一個工作薄
*/
public void createWorkBook() {
    this.wb = new SXSSFWorkbook();
}

2、編寫填充數據並導出函數

編寫該函數主要分為以下幾步:

  1. 創建工作表 sheet createSheet子函數
  2. 創建一行 row
  3. 將字段作為列表名(在Excel注解的name屬性中填寫的)填充進excel表格 createCell子函數
  4. 將數據庫中查詢到的數據填進Excel表中 fillDataToExcel子函數
  5. 將excel表格輸出到指定位置
public void exportExcel() {
    //1.創建sheet表
    //1.1 根據數據的多少,判斷需要創建幾張sheet, 向上取整
    int sheetNo = (int) Math.ceil(this.dataList.size() / sheetSize);
    for (int i = 0; i <= sheetNo; i++) {
        OutputStream os = null;
        try {
            //創建sheet表
            createSheet(sheetNo, i);
            //產生1行
            Row row = sheet.createRow(0);
            int column = 0;
            //2.將字段作為列表名填進表格
            for (Object[] field : this.fields) {
                Excel tempExcel = (Excel) field[1];
                //創建列,並將數據填充
                createCell(row, tempExcel, column++);
            }

            //3.將數據填充到excel表格中
            fillDataToExcel(i, row);
            //4.輸出
            //獲取文件名字
            String fileName = encodingFileName(sheetName);
            os =new FileOutputStream(getAbsoluteFile(fileName));
            this.wb.write(os);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //釋放資源
            if (wb != null){
                try {
                    wb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (os != null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
	創建sheet表,並根據sheet表的數量為sheet表起名字
*/
public void createSheet(int sheetNo, int index) {
    this.sheet = this.wb.createSheet();
    //創建表格的樣式,分別有,數據,列表頭
    createStyle(wb);
    if (index == 0) {//第一個在工作表
        this.wb.setSheetName(index, sheetName);

    } else {
        this.wb.setSheetName(index, sheetName + index);
    }
}

/**
     * 創建一列
     *
     * @param row    行
     * @param excel
     * @param column 列
     */
public void createCell(Row row, Excel excel, int column) {
    Cell cell = row.createCell(column);
    String name = excel.name();
    //設置單元格樣式
    cell.setCellStyle(styleMap.get("head"));
    //填充數據
    cell.setCellValue(name);
}

/**
     * 將數據填充至單元格
     *
     * @param index
     * @param row
     */
public void fillDataToExcel(int index, Row row) {
    //單元格開始位置
    int startNo = index * sheetSize;
    //單元格結束位置
    int endNo = Math.min(startNo + sheetSize, dataList.size());
    for (int j = startNo; j < endNo; j++) {//endNo - startNo  總行數
        //創建行,沒有列頭,從0開始
        row = this.sheet.createRow(j - startNo + 1);
        //得到導出的對象
        T vo = this.dataList.get(j);
        int column = 0; //列號,從0開始

        for (Object[] objects : this.fields) {
            //設置實體私有屬性可訪問
            Field field = (Field) objects[0];
            Excel excel = (Excel) objects[1];
            field.setAccessible(true);
            //添加單元格,(列)
            addCell(row, excel, vo, field, column++);
        }

    }
}


/**
     * 添加單元格
     *
     * @param row
     * @param vo
     * @param field
     * @param column
     * @return
     */
public Cell addCell(Row row, Excel excel, T vo, Field field, int column) {
    Cell cell = null;
    try {
        //創建單元格
        cell = row.createCell(column);
        //讀取對象中的屬性的值,如:
        //field 為id 則 value為 id 的值 類型為Object類型
        Object value = field.get(vo);
        if (excel.columnType() == Excel.ColumnType.NUMBER) {
            cell.setCellValue((Integer) value);
        }else if (excel.columnType() == Excel.ColumnType.STRING) {
            value = value == null ? value : value + excel.suffix();
            cell.setCellValue((String) value);
        }

    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    cell.setCellStyle(styleMap.get("data"));
    return cell;
}
/**
     * 創建表格的樣式,分別有,數據,列表頭
     * 數據樣式根據對齊方式,分別又分3種樣式
     *
     * @param wb
     */
public void createStyle(SXSSFWorkbook wb) {
    this.styleMap = new HashMap<>();
    CellStyle style = wb.createCellStyle();

    //1.設置數據格式
    //1.1設置對齊方式
    style.setAlignment(HorizontalAlignment.CENTER);//水平居中對齊
    style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中對齊

    //1.2單元格邊框樣式
    style.setBorderLeft(BorderStyle.THIN);//細線
    style.setLeftBorderColor(IndexedColors.BLUE.getIndex());//藍色
    style.setBorderRight(BorderStyle.THIN);//破折號-點
    style.setLeftBorderColor(IndexedColors.BLUE.getIndex());
    style.setBorderTop(BorderStyle.THIN);//
    style.setTopBorderColor(IndexedColors.BLUE.getIndex());
    style.setBorderBottom(BorderStyle.THIN);//虛線
    style.setBottomBorderColor(IndexedColors.BLUE.getIndex());

    //1.3設置數據字體大小
    Font dataFont = wb.createFont();
    dataFont.setFontHeightInPoints((short) 10);
    dataFont.setColor(IndexedColors.BLACK.getIndex());//設置字體顏色
    dataFont.setFontName("Arial");//設置字體
    style.setFont(dataFont);
    styleMap.put("data", style);

    //2.設置列表頭樣式
    style = wb.createCellStyle();
    style.cloneStyleFrom(styleMap.get("data"));
    //2.1設置單元格背景顏色
    style.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
    style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
    //2.2設置數據字體
    Font headFont = wb.createFont();
    headFont.setColor(IndexedColors.RED.getIndex());
    headFont.setBold(true);//加粗
    headFont.setFontName("Arial");
    headFont.setFontHeightInPoints((short) 10);//設置字體大小
    style.setFont(headFont);
    styleMap.put("head", style);

}

3、將excel表格輸出到指定位置

//輸出
//獲取文件名字
String fileName = encodingFileName(sheetName);
//獲取文件絕對路徑,並寫入文件流中
os =new FileOutputStream(getAbsoluteFile(fileName));
this.wb.write(os);

/**
     * 編碼文件名稱,使其具有唯一性
     * @param fileName
     * @return
     */
String encodingFileName(String fileName){
    fileName = UUID.randomUUID().toString() + "_" +fileName + ".xlsx";
    return fileName;
}

/**
	獲取文件絕對路徑
	fileName 文件名
*/
public String getAbsoluteFile(String fileName){
    //從yml文件中獲取絕對路徑
    String downloadPath = ExcelConfig.getDownLoad() + fileName;
    File desc = new File(downloadPath);
    //若文件路勁不存在,則創建
    if (!desc.getParentFile().exists()){
        desc.getParentFile().mkdirs();
    }
    return downloadPath;
}

注:ExcelConfig

此配置類可獲取yml中文件中所設置的路徑,使其可靈活配置路徑

yml中的的配置

excel:
  #文件路徑 實例(Windows配置D:/excelUp/uploadPath)此處可填寫服務器路徑
  profile: D:/excelUp/uploadPath

ExcelConfig配置類

@Component
@ConfigurationProperties(prefix = "excel")
public class ExcelConfig {
    //文件路徑
    private static String profile;
	
    //ConfigurationProperties注解需要此set函數
    public void setProfile(String profile) {
        this.profile = profile;
    }
	    
    public static String getProfile() {
        return profile;
    }

    public static String getDownLoad(){
        return getProfile() + "/download/";
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM