基于poi和JavaBean的excel导出

写在前面

  • 示例写出时间:2022-08-03

这仅仅是excel导出的一个示例, 记录一下

poi依赖

其他spring、lombok等依赖就不列举了

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.0</version>
</dependency>

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

核心类

注解ExcelColumn

package com.littlehow.excel;

import java.lang.annotation.*;

/**
 * excel字段注解
 * @author littlehow
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ExcelColumn {
    /**
     * @return - 表头
     */
    String title();

    /**
     * @return - 顺序
     */
    int order();

    /**
     * @return 日期格式化
     */
    String format() default "";

    /**
     * @return 行高
     */
    short height() default 450;

    /**
     * @return - 列宽
     */
    int width() default 4800;
}

列注解属性ExcelColumnMeta

package com.littlehow.excel;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;

/**
 *  列的基本属性
 * @author littlehow
 */
@Setter
@Getter
@Accessors(chain = true)
public class ExcelColumnMeta {
    /**
     * 表头
     */
    private String title;

    /**
     * 行高
     */
    private short height;

    /**
     * 列宽
     */
    private int width;

    /**
     * 日期格式化
     */
    private DateTimeFormatter formatter;

    public String getDateString(TemporalAccessor accessor) {
        if (formatter != null) {
            return formatter.format(accessor);
        }
        return accessor.toString();
    }
}

列字段属性ExcelMetaData

package com.littlehow.excel;

import lombok.Getter;
import lombok.Setter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.temporal.TemporalAccessor;

/**
 * 字段属性以及基本获取值的方法
 * @author littlehow
 */
@Setter
@Getter
public class ExcelMetaData {
    /**
     * 字段
     */
    private Field field;

    /**
     * 对应的get方法
     */
    private Method method;

    /**
     * 排序
     */
    private int order;

    /**
     * 日期顶级接口
     */
    private boolean isTemporalAccessor = false;

    /**
     * 是否为decimal类型
     */
    private boolean isDecimal = false;

    /**
     * 字段属性
     */
    private ExcelColumnMeta columnMeta;

    /**
     * 获取对象的值,做简单类型的格式化
     * @param obj - 对象
     * @return - obj对应该字段的格式化值
     */
    public String getString(Object obj) {
        if (obj == null) {
            return "";
        }
        Object value = getValue(obj);
        if (value == null) {
            return "";
        }

        if (isTemporalAccessor) {
            return columnMeta.getDateString((TemporalAccessor) value);
        } else if (isDecimal) {
            return ((BigDecimal) value).stripTrailingZeros().toPlainString();
        } else {
            return value.toString();
        }
    }

    /**
     * 获取对象的值, 优先get方法获取,没有get方法的将使用field获取,所以如果没有提供get方法,需要将字段访问属性设置为public
     * 这里就不对字段进行field.setAccessible设置
     * @param obj  - 对象
     * @return - 对应对应的值
     */
    private Object getValue(Object obj) {
        try {
            if (method != null) {
                return method.invoke(obj);
            } else {

                return field.get(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}


excel导出属性ExcelMeta

package com.littlehow.excel;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

/**
 * 记录表宽度、表头、行高以及excel字段信息
 * @author littlehow
 */
@Setter
@Getter
public class ExcelMeta {
    /**
     * 列宽
     */
    private List<Integer> width;

    /**
     * 行高
     */
    private short height;

    /**
     * 表头
     */
    private List<String> header;

    /**
     * 字段属性集
     */
    private List<ExcelMetaData> excelMetaData;
}


excel属性缓存类ExcelClassCache

package com.littlehow.excel;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author littlehow
 */
public class ExcelClassCache {
    private static Map<Class, ExcelMeta> EXCEL_MATE_CACHE = new HashMap<>();

    /**
     * 获取meta配置数据
     * @param clazz - 类
     * @return  -
     */
    static ExcelMeta getMeta(Class clazz) {
        ExcelMeta meta = EXCEL_MATE_CACHE.get(clazz);
        if (meta != null) {
            return meta;
        }
        meta = new ExcelMeta();
        // 解析class
        List<Field> fields = new ArrayList<>();
        getAllField(fields, clazz);
        List<ExcelMetaData> metaData = new ArrayList<>();
        fields.forEach(o -> {
            if (o.isAnnotationPresent(ExcelColumn.class)) {
                ExcelMetaData excelMetaData = new ExcelMetaData();
                ExcelColumn excelColumn = o.getAnnotation(ExcelColumn.class);
                ExcelColumnMeta columnMeta = new ExcelColumnMeta()
                        .setHeight(excelColumn.height())
                        .setTitle(excelColumn.title())
                        .setWidth(excelColumn.width());
                if (TemporalAccessor.class.isAssignableFrom(o.getType())) {
                    excelMetaData.setTemporalAccessor(true);
                    String format = excelColumn.format();
                    if (StringUtils.hasText(format)) {
                        columnMeta.setFormatter(DateTimeFormatter.ofPattern(format));
                    }
                } else if (o.getType() == BigDecimal.class) {
                    excelMetaData.setDecimal(true);
                }
                excelMetaData.setField(o);
                excelMetaData.setMethod(getMethod(o, clazz));
                excelMetaData.setColumnMeta(columnMeta);
                excelMetaData.setOrder(excelColumn.order());
                metaData.add(excelMetaData);
            }
        });
        Assert.notEmpty(metaData, "excel config not found");
        metaData.sort(Comparator.comparing(ExcelMetaData::getOrder));
        meta.setExcelMetaData(metaData);
        meta.setWidth(metaData.stream().map(o -> o.getColumnMeta().getWidth()).collect(Collectors.toList()));
        meta.setHeight(metaData.get(0).getColumnMeta().getHeight());
        meta.setHeader(metaData.stream().map(o -> o.getColumnMeta().getTitle()).collect(Collectors.toList()));
        EXCEL_MATE_CACHE.put(clazz, meta);
        return meta;
    }

    /**
     * 获取所有字段,包括父类的字段
     * @param fieldList -
     * @param clazz -
     */
    private static void getAllField(List<Field> fieldList, Class clazz) {
        if (clazz == Object.class) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        fieldList.addAll(Arrays.asList(fields));
        getAllField(fieldList, clazz.getSuperclass());

    }

    /**
     * 获取字段对应的get方法
     * @param field -
     * @param clazz -
     * @return -
     */
    private static Method getMethod(Field field, Class clazz) {
        try {
            String name = field.getName();
            if (name.length() > 1) {
                return  clazz.getMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1));
            } else {
                return clazz.getMethod("get" + name.toUpperCase());
            }
        } catch (Exception e) {
            // skip;
            return null;
        }
    }
}


excel导出类Excel

package com.littlehow.excel;

import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * excel导出工具
 * @author littlehow
 */
public class Excel<T> {
    private SXSSFWorkbook workbook;

    private SXSSFSheet sheet;

    private int rowIndex = 0;

    private boolean create = false;

    private ExcelMeta excelMeta;

    private Excel() {
        // 不开放
    }

    /**
     * 构建excel导出类
     * @param clazz - 导出类型
     * @param <T> -
     * @return - 导出类
     */
    public static <T> Excel<T> build(Class<T> clazz) {
        Excel<T> excel = new Excel<>();
        excel.excelMeta = ExcelClassCache.getMeta(clazz);
        excel.workbook = new SXSSFWorkbook();
        excel.sheet = excel.workbook.createSheet();
        // 设置列头以及列宽
        excel.fullHeader();
        return excel;
    }

    /**
     * 填充类容
     * @param content - 类容
     */
    public Excel<T> write(List<T> content) {
        Assert.isTrue(!create, "excel already created");
        if (CollectionUtils.isEmpty(content)) {
            return this;
        }
        XSSFCellStyle style = (XSSFCellStyle)workbook.createCellStyle();
        content.forEach(t -> {
            // 创建表头
            SXSSFRow row = sheet.createRow(this.rowIndex++);
            row.setHeight(excelMeta.getHeight());
            setContent(row, getContent(t), style);
        });
        return this;
    }

    /**
     * 输出 本方法没有关闭外部输出流
     * @param os - 输出流
     * @throws IOException  - io异常
     */
    public void create(OutputStream os) throws IOException {
        workbook.write(os);
        create = true;
    }


    /**
     * 填充表头以及设置列宽
     */
    private void fullHeader() {

        setColumnWidth(this.sheet, excelMeta.getWidth());
        // 设置表头
        SXSSFRow header = this.sheet.createRow(this.rowIndex++);
        header.setHeight(excelMeta.getHeight());
        XSSFCellStyle style = (XSSFCellStyle)workbook.createCellStyle();
        setContent(header, excelMeta.getHeader(), style);
    }

    /**
     * 获取字段内容
     * @param t - 对象
     * @return -
     */
    private List<String> getContent(T t) {
        List<ExcelMetaData> excelMetaData = excelMeta.getExcelMetaData();
        List<String> values = new ArrayList<>();
        excelMetaData.forEach(data -> values.add(data.getString(t)));
        return values;
    }


    /**
     * 设置内容
     * @param row        - 行
     * @param content    - 内容
     * @param style      - 样式
     */
    private static void setContent(SXSSFRow row, List<String> content, XSSFCellStyle style) {
        // 设置第一行
        for (int i = 0, len = content.size(); i < len; i++) {
            SXSSFCell cell = row.createCell(i);
            cell.setCellValue(content.get(i));
            // 创建一个单元格样式
            cell.setCellStyle(style);
            style.setAlignment(HorizontalAlignment.CENTER);
            style.setVerticalAlignment(VerticalAlignment.CENTER);
        }
    }


    /**
     * 设置列宽
     * @param sheet          - sheet
     * @param columnWidths   - 列宽配置
     */
    private static void setColumnWidth(SXSSFSheet sheet, List<Integer> columnWidths) {
        for (int i = 0, len = columnWidths.size(); i < len; i++) {
            sheet.setColumnWidth(i, columnWidths.get(i));
        }
    }
}

测试类

测试对象父类Biology

package com.littlehow.excel.test;

import com.littlehow.excel.ExcelColumn;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

/**
 * 生物对象
 * @author littlehow
 */
@Setter
@Getter
public class Biology {
    @ExcelColumn(title = "发布时间", order = 9, format = "yyyy-MM-dd HH:mm:ss", width = 5200)
    private LocalDateTime time;
}


测试对象子类Person

package com.littlehow.excel.test;

import com.littlehow.excel.ExcelColumn;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.time.LocalDate;

/**
 * @author littlehow
 */
@Getter
@Setter
@Accessors(chain = true)
public class Person extends Biology {

    @ExcelColumn(title = "用户编号", order = 2)
    private String userId;

    @ExcelColumn(title = "生日", order = 1, format = "yyyyMMdd")
    private LocalDate birthday;

    @ExcelColumn(title = "年龄", order = 4)
    private Integer age;

    @ExcelColumn(title = "公司", order = 6)
    private String company;

    @ExcelColumn(title = "存款", order = 5)
    private BigDecimal balance;
}

导出测试类ExcelTest

package com.littlehow.excel.test;

import com.littlehow.excel.Excel;

import java.io.FileOutputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author littlehow
 */
public class ExcelTest {
    public static void main(String[] args) throws Exception {
        List<Person> person = new ArrayList<>();
        person.add(new Person().setAge(18).setBirthday(LocalDate.now().minusYears(18)).setUserId("101").setCompany("正义公司").setBalance(BigDecimal.valueOf(123.5500)));
        person.add(new Person().setAge(20).setBirthday(LocalDate.now().minusYears(20)).setUserId("104").setCompany("正义公司").setBalance(BigDecimal.valueOf(88891.102900)));
        person.add(new Person().setAge(22).setBirthday(LocalDate.now().minusYears(22)).setUserId("110").setCompany("正义公司").setBalance(BigDecimal.valueOf(123.5570)));
        person.add(new Person().setAge(24).setBirthday(LocalDate.now().minusYears(24)).setUserId("120").setCompany("正义公司").setBalance(BigDecimal.valueOf(3333)));
        person.forEach(o -> o.setTime(LocalDateTime.now().minusDays(5)));
        Excel<Person> excel = Excel.build(Person.class);
        excel.write(person)
                 // 这里可以多次写入,所以不需要一次性将所有数据都加载到内存
                .write(person)
                .write(person)
                // 这里就会生成对应的excel文件
                .create(new FileOutputStream("/Users/littlehow/Desktop/person.xlsx"));
    }
}

导出结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值