笔记(一)-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