EasyExcel动态列导出

测试代码地址:https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn
官方文档:https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write

一、实现方式

1、根据需要导出的列找到返回类对象属性的ExcelPropertyColumnWidth注解,最终生成需要显示的列名和每列的列宽;
2、根据需要导出的列获取Excel中的行数据;
3、添加自定义单元格拦截策略(实现com.alibaba.excel.write.handler.WriteHandler接口)和数据类型转换策略(实现com.alibaba.excel.converters.Converter接口);
4、创建Excel的Sheet页,设置第一步获取的列宽;

二、代码实现

(一)添加基础数据类型转换器(LocalDate、LocalDateTime、LocalTime、Integer)

package com.xiaobai.easyexcel.dynamiccolumn;

import cn.hutool.core.date.DateTime;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * @author wangtw
 * @ClassName DataConverter
 * @description: 类型转换器
 * @date 2024/2/919:23
 */
public class DataConverter {

    public static class CoreConverter<T> implements Converter<T> {

        private final Class<T> tClass;

        public CoreConverter(Class<T> tClass) {
            this.tClass = tClass;
        }

        /**
         * 导出支持的类型
         * @return
         */
        @Override
        public Class supportJavaTypeKey() {
            return tClass;
        }

        /**
         * 导入支持的Excel类型
         * @return
         */
        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        /**
         * 导入类型转换
         * @param cellData
         * @param excelContentProperty
         * @param globalConfiguration
         * @return
         * @throws Exception
         */
        @Override
        public T convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
            if (cellData.getData() instanceof LocalDate) {
                return (T) LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
            if (cellData.getData() instanceof LocalTime) {
                return (T) LocalTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("HH:mm:ss"));
            }
            if (cellData.getData() instanceof LocalDateTime) {
                return (T) LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
            if (cellData.getData() instanceof Integer) {
                return (T) Integer.valueOf(cellData.getStringValue());
            }
            return null;
        }

        /**
         * 导出类型转换
         * @param obj
         * @param excelContentProperty
         * @param globalConfiguration
         * @return
         * @throws Exception
         */
        @Override
        public CellData convertToExcelData(T obj, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {

            if (obj instanceof LocalDate) {
                return new CellData(((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            }

            if (obj instanceof LocalDateTime) {
                return new CellData(((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }

            if (obj instanceof LocalTime) {
                return new CellData(((LocalTime) obj).format(DateTimeFormatter.ofPattern("HH:mm:ss")));
            }

            if (obj instanceof Integer) {
                return new CellData(String.valueOf(obj));
            }

            return null;
        }
    }

    /**
     * localDate类型转换
     */
    public static class LocalDateConverter extends CoreConverter<LocalDate> {

        public LocalDateConverter() {
            super(LocalDate.class);
        }
    }

    /**
     * localTime类型转换
     */
    public static class LocalTimeConverter extends CoreConverter<LocalTime> {

        public LocalTimeConverter() {
            super(LocalTime.class);
        }
    }

    /**
     * LocalDateTime类型转换
     */
    public static class LocalDateTimeConverter extends CoreConverter<LocalDateTime> {

        public LocalDateTimeConverter() {
            super(LocalDateTime.class);
        }
    }

    /**
     * Integer
     */
    public static class IntegerConverter extends CoreConverter<Integer> {

        public IntegerConverter() {
            super(Integer.class);
        }
    }
}

(二)导出实现代码

package com.xiaobai.easyexcel.dynamiccolumn;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.istack.internal.NotNull;
import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
import org.apache.poi.util.IOUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @author wangtw
 * @ClassName DynamicColumnExport
 * @description: EasyExcel动态列导出
 * @date 2024/2/918:07
 */
public class DynamicColumnExport {

    /**
     *
     * @param d 查询数据方法参数
     * @param vClass 返回类型
     * @param getDataFun 查询数据函数式接口
     * @param outputStream 输出流
     * @param includeColumns 需要导出的列
     * @param writeHandlerList 自定义拦截器
     * @param converterList 自定义数据格式化转换器
     * @param 
     * @param 
     * @param 
     */
    public static <D, U, V> void export(D d, Class<V> vClass,
                                        @NotNull Function<D, List<V>> getDataFun,
                                        @NotNull OutputStream outputStream,
                                        @NotNull List<String> includeColumns,
                                        @Nullable List<? extends WriteHandler> writeHandlerList,
                                        @Nullable List<? extends Converter> converterList) {


        /**
         * 1、根据需要导出的列获取每列的列名和单元格的列宽
         */
        // 单元格宽度
        int columnIndex = 0;
        Map<Integer, Integer> columnWidthMap = new HashMap<>();
        //  获取表格头
        List<List<String>> headList = new ArrayList<>();
        List<Field> columnList = new ArrayList<>();
        Field[] declaredFields = vClass.getDeclaredFields();
        for (String includeColumn : includeColumns) {
            Optional<Field> includeColumnOptional = Arrays.stream(declaredFields).filter(f -> f.getName().equals(includeColumn))
                    .findFirst();
            if (includeColumnOptional.isPresent()) {
                Field field = includeColumnOptional.get();
                field.setAccessible(true);
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                if (!ObjectUtils.isEmpty(excelProperty)) {
                    // 列名
                    String[] columNameArray = excelProperty.value();
                    headList.add(Arrays.asList(columNameArray));

                    // 可导出的列
                    columnList.add(field);

                    // 保存每列的宽度
                    ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class);
                    columnWidthMap.put(columnWidth == null ? -1 : columnWidth.value(), columnIndex++);
                }
            }
        }

        /**
         * 2、根据需要导出的列获取需要显示的数据
         */
        List<List<Object>> exportDataList = new ArrayList<>();
        // 执行函数式接口获取需要导出的数据
        List<V> dataCollection = getDataFun.apply(d);

        for (V v : dataCollection) {

            // 拼装每行的数据
            List<Object> dataSubList = new ArrayList<>();
            for (Field field : columnList) {
                try {
                    Object columnValue = field.get(v);
                    dataSubList.add(Optional.ofNullable(columnValue).orElse(""));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            exportDataList.add(dataSubList);
        }

        // 表格处理策略
        ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream).head(headList);

        if (!ObjectUtils.isEmpty(writeHandlerList)) {
            writeHandlerList.forEach(writerBuilder::registerWriteHandler);
        }

        // 类型转换策略
        if (ObjectUtils.isEmpty(converterList)) {
            writerBuilder.registerConverter(new DataConverter.IntegerConverter());
            writerBuilder.registerConverter(new DataConverter.LocalDateConverter());
            writerBuilder.registerConverter(new DataConverter.LocalTimeConverter());
            writerBuilder.registerConverter(new DataConverter.LocalDateTimeConverter());
        } else {
            converterList.forEach(writerBuilder::registerConverter);
        }

        // 创建Sheet页
        String sheetName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        // 设置列宽
        writeSheet.setColumnWidthMap(columnWidthMap);

        // 把数据写入到Sheet页中
        ExcelWriter excelWriter = writerBuilder.build();
        excelWriter.write(exportDataList, writeSheet);

        // 关闭流
        excelWriter.finish();

        // 关闭输出流
        IOUtils.closeQuietly(outputStream);

    }
}

三、测试

(一)设置表头和内容策略

    /**
     * 设置颜色
     * @return
     */
    private WriteHandler setColor() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为白色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short)18);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short)15);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        return horizontalCellStyleStrategy;
    }

(二)设置单元格样式策略

    public static class CellHandler implements CellWriteHandler {

        @Override
        public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
        }

        @Override
        public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        }

        @Override
        public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        }

        @Override
        public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
            CellStyle cellStyle = cell.getCellStyle();
            cellStyle.setBorderBottom(BorderStyle.THIN);
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
        }
    }

(四)实体类

可选择某几种属性进行导出

package com.xiaobai.easyexcel.dynamiccolumn;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

/**
 * @author wangtw
 * @ClassName DynamicData
 * @description: EasyExcel动态列导出
 * @date 2024/2/914:59
 */
@AllArgsConstructor
@Builder
@Data
public class DynamicData {

    @ExcelProperty("id")
    private Integer id;

    @ColumnWidth(30)
    @ExcelProperty("姓名")
    private String realName;

    @ColumnWidth(30)
    @ExcelProperty("性别")
    private String sex;

    @ColumnWidth(30)
    @ExcelProperty("年龄")
    private int age;

    @ColumnWidth(50)
    @ExcelProperty("单位名称")
    private String orgName;

    @ColumnWidth(50)
    @ExcelProperty("部门名称")
    private String deptName;
}

(五)数据准备

    private List<DynamicData> getData(String condition) {
        List<DynamicData> dynamicDataList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            DynamicData data = DynamicData.builder()
                    .id(i)
                    .age(random.nextInt(60))
                    .sex("男")
                    .realName("王" + i)
                    .orgName("单位" + i)
                    .deptName("部门" + i)
                    .build();
            dynamicDataList.add(data);

        }
        return dynamicDataList;
    }

(六)测试代码(选择orgName、deptName、realName、sex进行导出)

    @Test
    public void exportTest() {
        File file = new File("测试.xlsx");
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        List<String> includeColumns = new ArrayList<>();
        includeColumns.add("orgName");
        includeColumns.add("deptName");
        includeColumns.add("realName");
        includeColumns.add("sex");

        List<WriteHandler> writeHandlers = new ArrayList<>();
        writeHandlers.add(setColor());

        writeHandlers.add(new CellHandler());

        DynamicColumnExport.export(null, DynamicData.class,
                this::getData, outputStream, includeColumns, writeHandlers, null);
    }

(七)效果

EasyExcel动态列导出_第1张图片

你可能感兴趣的:(文件操作,EasyExcel)