今天看啥  ›  专栏  ›  swotXu

自定义注解 - 实体转Excel

swotXu  · CSDN  ·  · 2019-12-30 10:15

一、环境准备

由于平时工作中,经常会有这种,将某个列表的数据导出成excel的需求。此功能就属于比较通用的功能了,于是呢,为了避免重复代码,就设计了此种方式进行实现。

博主这里创建的 springboot 2.2.1.RELEASE 项目。使用的 jdk1.8 + poi 3.16 , 另外日志用到了 lombok 1.18.10

1、JDK安装配置

参考我的另外一篇博客:
链接: JDK安装配置-swotXu .

2、MAVEN安装配置

参考我的另外一篇博客:
链接: MAVEN安装配置-swotXu .

3、POM依赖引入

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.16</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

二、代码演示

先来看看怎么使用。大家如果觉得使用比较方便有趣,咱们再看看怎么实现的。
这里使用的几个封装类简单介绍下:

  • ExcelParse2SheetAdapte 对象
    实体数据解析与 ExcelSheet 的适配器,用于将实体数据集 转换为 ExcelSheet 对象
    提供 ExcelSheet getExcelSheet(String title, boolean hasOrderNum) 方法获取对象。
  • ExcelSheet 对象
    承载excel映射的对象,封装了对于POI相关操作,用于生成excel。
  • ExcelHandle 对象
    工具类,用于将 ExcelSheet 对象输出生成具体的excel文件。
    支持生成到具体磁盘目录、输出到指定流、web端直接输出下载三种方式。
  • HandleTimer 对象
    HandleTimer.runTime() 是用来统计方法耗时的一个工具类
    详细信息可参考我的另外一篇文章:
    链接: JDK1.8 - lamda 接口耗时统计-swotXu .
import com.swotxu.verifyideas.common.algorithm.timer.HandleTimer;
import com.swotxu.verifyideas.demo02.pojo.PeopleEntity;
import com.swotxu.verifyideas.demo02.utils.ExcelHandle;
import com.swotxu.verifyideas.demo02.utils.ExcelParse2SheetAdapte;
import com.swotxu.verifyideas.demo02.utils.ExcelSheet;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

/**
 * @Classname: springboot-parent
 * @Date: 2019/12/16 0016 13:48
 * @Author: swotXu
 */
public class TestMain {
    /**
     * 生成测试数据  list
     * @return
     */
    public static List<PeopleEntity> getTestDataList(){
        List<PeopleEntity> dataList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            PeopleEntity entity = new PeopleEntity();
            entity.setIdCard(UUID.randomUUID().toString());
            entity.setName("name" + i);
            entity.setAge(random.nextInt(100));
            entity.setSex(random.nextBoolean()? "男" : "女");

            dataList.add(entity);
        }
        return dataList;
    }
    public static void execute(){
        // 获取测试数据
        List<PeopleEntity> dataList1 = getTestDataList();
        // 根据实体数据,获取excelSheet的适配器
        ExcelParse2SheetAdapte<PeopleEntity> sheetAdapte1 = new ExcelParse2SheetAdapte(dataList1, PeopleEntity.class);
        // 通过适配器拿到 excelSheet 对象,设置标题,开启序号列
        ExcelSheet sheet1 = sheetAdapte1.getExcelSheet("测试table1", ExcelSheet.USE_ORDER_NUMBER);
        
        // 获取测试数据
        List<PeopleEntity> dataList2 = getTestDataList();
        // 根据实体数据,获取excelSheet的适配器
        ExcelParse2SheetAdapte<PeopleEntity> sheetAdapte2 = new ExcelParse2SheetAdapte(dataList2, PeopleEntity.class);
        // 通过适配器拿到 excelSheet 对象,设置标题,关闭序号列
        ExcelSheet sheet2 = sheetAdapte2.getExcelSheet("测试table2", ExcelSheet.DEL_ORDER_NUMBER);
        // 可以手动设置单元格样式, 也可不设置 - 使用默认样式
        sheet2.setBodyColumnTopStyleFun(workbook -> {
            // 设置字体
            HSSFFont font = workbook.createFont();
            font.setFontHeightInPoints((short)10); //设置字体大小
            font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); //字体加粗
            font.setFontName("Courier New"); //设置字体名字
            //设置样式;
            HSSFCellStyle style = workbook.createCellStyle();
            style.setBorderBottom(HSSFCellStyle.BORDER_THIN); //设置底边框;
            style.setFont(font); //在样式用应用设置的字体;

            return style;
        });
        

        // 通过 ExcelHandle 设置sheet页,并输出到指定目录
        ExcelHandle.builder().setSheet(sheet1).setSheet(sheet2).build("D:\\test\\files\\test.xls");
    }

    public static void main(String[] args) {
    	// HandleTimer.runTime() 是用来统计方法耗时的一个工具类
        HandleTimer.runTime(TestMain::execute);
    }
}


import com.swotxu.verifyideas.demo02.annotation.ExcelColunm;
import lombok.Getter;
import lombok.Setter;

/**
 * @Date: 2019/12/14 22:47
 * @Author: swotXu
 */
public class PeopleEntity {
    @Getter
    @Setter
    @ExcelColunm(colunmName = "姓名", order = 20)
    private String name;

    @Getter
    @Setter
    @ExcelColunm(colunmName = "性别", order = 10)
    private String sex;

    @Getter
    @Setter
    @ExcelColunm(colunmName = "年龄", order = 40)
    private int age;

    @Getter
    @Setter
    @ExcelColunm(colunmName = "身份证", order = 30)
    private String idCard;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

上面代码演示生成有两个sheet页的excel。
sheet1,使用默认样式,开启序号列,结果如下
在这里插入图片描述
sheet2,使用指定样式,关闭序号列,结果如下
在这里插入图片描述

三、代码实现

咱们先看看类结构图:
在这里插入图片描述

1、@ExcelColunm 注解

我们先来创建一个 @ExcelColunm 注解,用于标记实体属性上。
其中:
colunmName 用于标识实体属性与excel中table表列的映射关系
order 用于标识excel中table表列的排列顺序

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 此注解标记于实体属性上,用于声明实体转Excel表头的对应关系
 * @Date: 2019/12/14 21:51
 * @Author: swotXu
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ExcelColunm {
    /**
     * Excel 列名
     * @return
     */
    String colunmName() default "";

    /**
     * 列排序 由小到大
     * @return
     */
    int order() default 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

2、ExcelColunmParse

接下来,编写解析注解的工具类
ExcelColunmParse 用于解析实体上的 @ExcelColunm 注解

import com.swotxu.verifyideas.demo02.annotation.ExcelColunm;
import com.swotxu.verifyideas.demo02.pojo.PeopleEntity;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ExcelColunm 的解析工具
 *
 * @Date: 2019/12/14 21:53
 * @Author: swotXu
 */
@Slf4j
public class ExcelColunmParse {
    public static void main(String[] args) {
        List<ParseResult> results = parseExcelColunm(PeopleEntity.class);
        log.info("{}", results);
    }

    /**
     * 解析实体类中 @ExcelColunm
     * @param aClass
     * @return
     */
    public static List<ParseResult> parseExcelColunm(Class<?> aClass){
        return Arrays.stream(aClass.getDeclaredFields())
                // 类型转换:将 field 对象转换为 ParseResult 对象
                .map(field -> {
                    if (field.isAnnotationPresent(ExcelColunm.class)) {
                        ExcelColunm excelColunm = field.getAnnotation(ExcelColunm.class);
                        ParseResult parseResult = new ParseResult(field.getName()
                                , excelColunm.colunmName(), excelColunm.order());
                        return parseResult;
                    }
                    return null;
                })
                // 过滤空对象
                .filter(parseResult -> parseResult != null)
                // 根据 order 排序
                .sorted((r1, r2) -> r1.getOrder() - r2.getOrder())
                .collect(Collectors.toList());
    }
    /**
     * @ExcelColunm 解析后的封装对象
     */
    public static class ParseResult {
        @Getter
        @Setter
        private String sourceKey;

        @Getter
        @Setter
        private String colunmName;

        @Getter
        @Setter
        private int order;

        private ParseResult(String sourceKey, String colunmName, int order) {
            this.sourceKey = sourceKey;
            this.colunmName = colunmName;
            this.order = order;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

3、ExcelSheet

然后,创建 ExcelSheet 对象,封装poi对excel的操作

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * @Date: 2019/12/15 17:01
 * @Author: swotXu
 */
@Slf4j
@SuppressWarnings("deprecation")
public class ExcelSheet {
    /**开启序号列 默认开启*/
    public static final boolean USE_ORDER_NUMBER = true;
    /**关闭序号列*/
    public static final boolean DEL_ORDER_NUMBER = false;
    public static final String ORDER_NUMBER_NAME = "序号";

    // 是否开启序号列
    private boolean hasOrderNum;
    // 显示的导出表的标题
    private String title;
    // 导出表的列名
    private String[] rowNames;
    // 数据集
    private List<List<Object>> dataList;

    /** 列头单元格样式 */
    @Setter
    private Function<HSSFWorkbook, HSSFCellStyle> headColumnTopStyleFun;
    /** 列数据信息单元格样式 */
    @Setter
    private Function<HSSFWorkbook, HSSFCellStyle> bodyColumnTopStyleFun;

    //构造方法,传入要导出的数据
    public ExcelSheet(String title, String[] rowNames, List<List<Object>> dataList, boolean hasOrderNum){
        this.title = title;
        this.rowNames = rowNames;
        this.dataList = dataList;
        this.hasOrderNum = hasOrderNum;
        this.headColumnTopStyleFun = ExcelSheet::getHeadColumnTopStyle;
        this.bodyColumnTopStyleFun = ExcelSheet::getBodyColumnTopStyle;
    }

    /**
     * 生成excel数据
     * @return
     */
    public HSSFWorkbook createExcel(){
        return createExcel(new HSSFWorkbook());
    }

    /**
     * 生成excel数据
     * @param workbook  创建工作簿对象
     * @return
     */
    public HSSFWorkbook createExcel(HSSFWorkbook workbook){
        try{
            // sheet 单元格样式定义, 表头 和 表数据样式
            HSSFCellStyle headColumnTopStyle = this.headColumnTopStyleFun.apply(workbook);
            HSSFCellStyle bodyColumnTopStyle = this.bodyColumnTopStyleFun.apply(workbook);
            //单元格样式对象
            HSSFSheet sheet = workbook.createSheet(title);    // 创建工作表
            // 产生表格标题行
            setColumnHeadData(sheet, headColumnTopStyle, rowNames.length);
            //将查询出的数据设置到sheet对应的单元格中
            setColumnBodyData(sheet, bodyColumnTopStyle);
            //让列宽随着导出的列长自动适应
            setColumnAutoWidth(sheet, rowNames.length);

            return workbook;
        }catch(Exception e){
            log.warn("Create workbook failed!", e);
        }
        return null;
    }

    /**
     * 生成excel table 表头
     * @param sheet     sheet对象
     * @param headColumnTopStyle    表头样式
     * @param columnNum     列数量
     */
    private void setColumnHeadData(HSSFSheet sheet, HSSFCellStyle headColumnTopStyle, int columnNum){
        // 产生表格标题行
        HSSFRow rowm = sheet.createRow(0);
        HSSFCell cellTiltle = rowm.createCell(0);
        // 设置title
        sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, hasOrderNum?rowNames.length:rowNames.length-1));
        cellTiltle.setCellStyle(headColumnTopStyle);
        cellTiltle.setCellValue(title);

        HSSFRow rowRowName = sheet.createRow(2);  // 在索引2的位置创建行(最顶端的行开始的第二行)
        // 若开启序号,将列头设置到sheet的单元格中
        if(hasOrderNum){
            setSerialNumber(rowRowName, headColumnTopStyle);
        }
        HSSFCell cellRowName;
        for(int n=0; n<columnNum; n++){
            //  创建列头对应个数的单元格  -- 若开启序号列,则 n+1
            cellRowName = rowRowName.createCell(hasOrderNum? n + 1 : n);
            cellRowName.setCellType(HSSFCell.CELL_TYPE_STRING);              //设置列头单元格的数据类型
            cellRowName.setCellValue(new HSSFRichTextString(rowNames[n]));   //设置列头单元格的值
            cellRowName.setCellStyle(headColumnTopStyle);                     //设置列头单元格样式
        }
    }
    /**
     * 将查询出的数据设置到sheet对应的单元格中
     *
     * @param sheet     sheet对象
     * @param bodyColumnTopStyle    数据列样式
     */
    private void setColumnBodyData(HSSFSheet sheet, HSSFCellStyle bodyColumnTopStyle){
        for(int i=0;i<dataList.size();i++){
            List<Object> objList = dataList.get(i);//遍历每个对象
            HSSFRow row = sheet.createRow(i+3);//创建所需的行数

            HSSFCell cell;   //设置单元格的数据类型
            if(hasOrderNum){
                cell = row.createCell(0, HSSFCell.CELL_TYPE_NUMERIC);
                cell.setCellValue(i+1);
                cell.setCellStyle(bodyColumnTopStyle);
            }
            for(int j=0; j < objList.size(); j++){
                cell = row.createCell(hasOrderNum? j + 1 : j,HSSFCell.CELL_TYPE_STRING);
                if(!Objects.isNull(objList.get(j))){
                    cell.setCellValue(objList.get(j).toString());                        //设置单元格的值
                }
                cell.setCellStyle(bodyColumnTopStyle);                                    //设置单元格样式
            }
        }
    }

    /**
     * 让列宽随着导出的列长自动适应
     * @param sheet     sheet对象
     * @param columnNum 列数量
     */
    private void setColumnAutoWidth(HSSFSheet sheet, int columnNum){
        for (int colNum = 0; colNum < columnNum; colNum++) {
            int columnWidth = sheet.getColumnWidth(colNum) / 256;
            for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
                HSSFRow currentRow;
                //当前行未被使用过
                if (sheet.getRow(rowNum) == null) {
                    currentRow = sheet.createRow(rowNum);
                } else {
                    currentRow = sheet.getRow(rowNum);
                }
                if (currentRow.getCell(colNum) != null) {
                    HSSFCell currentCell = currentRow.getCell(colNum);
                    if (currentCell != null && currentCell.getCellType() == HSSFCell.CELL_TYPE_STRING && currentCell.getStringCellValue() != null) {
                        int length = currentCell.getStringCellValue().getBytes().length;
                        if (columnWidth < length) {
                            columnWidth = length;
                        }
                    }
                }
            }
            if(colNum == 0){
                try{
                    columnWidth = (columnWidth-2) * 256;
                }catch(Exception e){
                    columnWidth = 1024;
                    e.printStackTrace();
                }
                sheet.setColumnWidth(colNum, columnWidth);
            }else{
                try{
                    columnWidth = (columnWidth+4) * 256;
                }catch(Exception e){
                    columnWidth = 1024;
                    e.printStackTrace();
                }
                sheet.setColumnWidth(colNum, columnWidth);
            }
        }
    }

    /**
     * 设置序号列
     * @param rowRowName    行对象
     * @param columnTopStyle    单元格样式对象
     */
    private static void setSerialNumber(HSSFRow rowRowName, HSSFCellStyle columnTopStyle){
        HSSFCell cellRowName = rowRowName.createCell(0);
        cellRowName.setCellType(HSSFCell.CELL_TYPE_STRING);                //设置列头单元格的数据类型
        HSSFRichTextString text = new HSSFRichTextString(ORDER_NUMBER_NAME);
        cellRowName.setCellValue(text);                                    //设置列头单元格的值
        cellRowName.setCellStyle(columnTopStyle);
    }

    /*
     * 默认列头单元格样式
     */
    private static HSSFCellStyle getHeadColumnTopStyle(HSSFWorkbook workbook) {
        // 设置字体
        HSSFFont font = workbook.createFont();
        //设置字体大小
        font.setFontHeightInPoints((short)11);
        //字体加粗
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        //设置字体名字
        font.setFontName("Courier New");
        //设置样式;
        HSSFCellStyle style = workbook.createCellStyle();
        //设置底边框;
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        //设置底边框颜色;
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        //设置左边框;
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        //设置左边框颜色;
        style.setLeftBorderColor(HSSFColor.BLACK.index);
        //设置右边框;
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        //设置右边框颜色;
        style.setRightBorderColor(HSSFColor.BLACK.index);
        //设置顶边框;
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        //设置顶边框颜色;
        style.setTopBorderColor(HSSFColor.BLACK.index);
        //在样式用应用设置的字体;
        style.setFont(font);
        //设置自动换行;
        style.setWrapText(false);
        //设置水平对齐的样式为居中对齐;
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        //设置垂直对齐的样式为居中对齐;
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

        return style;
    }
    /*
     * 默认列数据信息单元格样式
     */
    private static HSSFCellStyle getBodyColumnTopStyle(HSSFWorkbook workbook) {
        // 设置字体
        HSSFFont font = workbook.createFont();
        //设置字体大小
        //font.setFontHeightInPoints((short)10);
        //字体加粗
        //font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        //设置字体名字
        font.setFontName("Courier New");
        //设置样式;
        HSSFCellStyle style = workbook.createCellStyle();
        //设置底边框;
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        //设置底边框颜色;
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        //设置左边框;
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        //设置左边框颜色;
        style.setLeftBorderColor(HSSFColor.BLACK.index);
        //设置右边框;
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        //设置右边框颜色;
        style.setRightBorderColor(HSSFColor.BLACK.index);
        //设置顶边框;
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        //设置顶边框颜色;
        style.setTopBorderColor(HSSFColor.BLACK.index);
        //在样式用应用设置的字体;
        style.setFont(font);
        //设置自动换行;
        style.setWrapText(false);
        //设置水平对齐的样式为居中对齐;
        style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        //设置垂直对齐的样式为居中对齐;
        style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        return style;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286

4、ExcelParse2SheetAdapte

接下来,创建 ExcelParse2SheetAdapte ,将实体对象集转换为 ExcelSheet 对象

package com.swotxu.verifyideas.demo02.utils;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Date: 2019/12/15 23:03
 * @Author: swotXu
 */
@Slf4j
public class ExcelParse2SheetAdapte<T> {
    /**
     * excel表头
     */
    private String[] rowsName;
    /**
     * excel数据体
     */
    private List<List<Object>>  dataList;

    public ExcelParse2SheetAdapte(List<T> entityList, Class<T> aClass){
        entity2ExcelSheetAdapte(entityList, aClass);
    }
    /**
     * 实体对象与ExcelSheet适配 生成表头及表单数据
     * @param entityList
     * @param aClass
     */
    private void entity2ExcelSheetAdapte(List<T> entityList, Class<T> aClass){
        List<ExcelColunmParse.ParseResult> parseResults = ExcelColunmParse.parseExcelColunm(aClass);
        // Excel 表头
        String[] rowsName = parseResults.stream().<String>map(result -> result.getColunmName()).toArray(String[]::new);
        // Excel 数据集
        List<List<Object>> dataList = new ArrayList(entityList.size());
        List<Object> datas;
        // 解析数据实体集
        for (T entity : entityList) {
            datas = new ArrayList(rowsName.length);
            // 解析数据实体属性
            for (ExcelColunmParse.ParseResult parseResult : parseResults) {
                try {
                    Field f = aClass.getDeclaredField(parseResult.getSourceKey());
                    f.setAccessible(true);
                    Object o = f.get(entity);
                    datas.add(Objects.isNull(o)? "" : o);
                } catch (Exception e) {
                    log.warn("Failed to get entity properties!", e);
                }
            }
            dataList.add(datas);
        }
        this.rowsName = rowsName;
        this.dataList = dataList;
    }
    /**
     * 获取ExcelSheel对象
     * @param title     生成标题名
     * @param hasOrderNum     是否生成排序号
     * @return
     */
    public ExcelSheet getExcelSheet(String title, boolean hasOrderNum){
        return new ExcelSheet(title, rowsName, dataList, hasOrderNum);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

5、ExcelHandle

最后,通过工具类 ExcelHandle ,将 ExcelSheet 对象生成excel,输出到指定目标

package com.swotxu.verifyideas.demo02.utils;

import com.swotxu.verifyideas.common.file.utils.FileStreamUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @Date: 2019/12/14 23:13
 * @Author: swotXu
 */
@Slf4j
public class ExcelHandle {
    @Getter
    private HSSFWorkbook workbook = new HSSFWorkbook();

    private ExcelHandle(){}

    public static ExcelHandle builder(){
        return new ExcelHandle();
    }

    /**
     * 设置 sheet 页
     * @param sheet
     * @return
     */
    public ExcelHandle setSheet(ExcelSheet sheet){
        sheet.createExcel(this.workbook);
        return this;
    }
    /**
     * 导出到指定路径
     * @param path
     */
    public boolean build(String path){
        try {
            FileOutputStream out = new FileOutputStream(new File(path));
            return build(out);
        } catch (FileNotFoundException e) {
            log.warn("Failed to get file stream!", e);
        }
       return false;
    }
    /**
     * 导出到指定输出流
     * @param out
     */
    public boolean build(OutputStream out){
        boolean isOk = false;
        try{
            workbook.write(out);
            out.flush();
            isOk = true;
        } catch (IOException e){
            log.warn("workbook write failure!", e);
        } finally {
            FileStreamUtil.close(out);
        }
        return isOk;
    }
    /**
     * 页面导出下载 Excel
     * @param response
     * @param fileName  无后缀文件名
     */
    public boolean build(HttpServletResponse response, String fileName) {
        boolean isOk = false;
        OutputStream out = null;
        try{
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.addHeader(
                    "Content-Disposition",
                    "attachment;filename=" + new String(fileName.getBytes("GBK"), "ISO-8859-1") + ".xls");
            out = response.getOutputStream();
            workbook.write(out);
            out.flush();
            isOk = true;
        } catch (IOException e){
            e.printStackTrace();
        } finally {
            FileStreamUtil.close(out);
        }
        return isOk;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

6、FileStreamUtil

补充下,评论区有小伙伴留言询问,这个类是做什么的?
这个工具类,是用来统一关闭IO流资源的,下面贴一下源码

package com.swotxu.verifyideas.common.file.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;

/**
 * @Date: 2019/12/16 21:25
 * @Author: swotXu
 */
@Slf4j
public class FileStreamUtil {

    public static void close(OutputStream out){
        if (null != out){
            try {
                out.close();
            } catch (IOException e) {
                log.warn("OutputStream close failed!", e);
            }
        }
    }
    public static void close(InputStream in){
        if (null != in){
            try {
                in.close();
            } catch (IOException e) {
                log.warn("InputStream close failed!", e);
            }
        }
    }
    public static void close(Writer writer){
        if (null != writer){
            try {
                writer.close();
            } catch (IOException e) {
                log.warn("Writer close failed!", e);
            }
        }
    }
    public static void close(Reader reader){
        if (null != reader){
            try {
                reader.close();
            } catch (IOException e) {
                log.warn("Reader close failed!", e);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

至此,通过自定义注解 @ExcelColunm 将实体集转为 excel 功能就完成了。
大家如果有什么疑问,欢迎评论留言!别忘了收藏关注~




原文地址:访问原文地址
快照地址: 访问文章快照