easypoi下载excel 动态自定义列

最近用easypoi做了个下载功能,感觉这个工具包还是挺方便的。

用户有个需求是,自己选择下载哪些列,选择下载的时候自己勾选列名。

阅读easypoi教程,2.4注解变种-更自由的导出,提供了ExcelExportEntity这种实现,相当于代码层面动态生成model。

感觉写起来还是很繁琐,没有model加@Excel注解这种一目了然。

所以想到动态修改Class文件,改变@Excel注解的属性值来显隐列。

主要的实现原理是:修改字节码文件,所以记得恢复

        1、所有需要下载的列,按实体类模型设计加@Excel或@ExcelCollection或@ExcelEntity注解。

        2、默认情况下,按照注解导出Excel。

        3、若某一列不需要导出,如上面的name列,那么将@Excel注解的name属性修改为:主讲老师_ignore,但是主讲老师这个原始值需要记录下来。

        4、用修改后的TeacherEntity导出Excel。

        5、恢复@Excel注解的name属性为:主讲老师

 

上代码,实现如下:

Model是这个样子:

@ExcelTarget("teacherEntity")

public class TeacherEntity implements java.io.Serializable {

        /** name */

        @Excel(name = "主讲老师", needMerge=true)

        private String name;

}

 

自定义工具类,提供如下通用方法:

1、修改注解的name值,并记录原值

/**
     * 修改fields上@Excel注解的name属性,不需要下载的列,name修改增加_ignore.
     * 保存原来的@Excel注解name属性值,本次生成后用来恢复
     * @Params
     *     headers:用户勾选,由前端传来的列名,列名的key必须和Model字段对应
     *     clazz:model实体类
     *     excelMap:用来记录原值的map,因为用到了递归,这里返回值作为参数传入
     * @return Map<String, String> 原实体类字段名和@Excel注解name属性值的映射关系<字段名,@Excel注解name属性值>
     */
    public static  Map<String, String> dynamicChangeAndSaveSourceAnnotation(JSONArray headers, Class clazz, Map<String, String> excelMap) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // @Excel注解
            if (field.isAnnotationPresent(Excel.class)) {
                boolean flag = true;
                for (int i = 0; i < headers.size(); i++) {
                    JSONObject header = (JSONObject) headers.get(i);
                    if (field.getName().equals(header.get("key"))) {
                        flag = false;
                        break;
                    }
                }
                // 下载列不包括该字段,进行隐藏,并记录原始值
                if (flag) {
                    Excel annotation = field.getAnnotation(Excel.class);
                    // 保存注解
                    excelMap.put(field.getName(), annotation.name());
                    InvocationHandler handler = Proxy.getInvocationHandler(annotation);
                    changeAnnotationValue(handler, field.getName() + "_ignore");
                }
            // @ExcelCollection注解
            } else if (field.isAnnotationPresent(ExcelCollection.class) && field.getType().isAssignableFrom(List.class)) {
                Type type = field.getGenericType();
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;
                    Class collectionClazz = (Class) pt.getActualTypeArguments()[0];
                    // 解决@ExcelCollection如果没有需要下载列的异常,java.lang.IllegalArgumentException: The 'to' col (15) must not be less than the 'from' col (16)
                    // 如果没有需要下载列,将@ExcelCollection忽略
                    Field[] collectionFields = collectionClazz.getDeclaredFields();
                    boolean flag = false;
                    out: for (Field temp : collectionFields) {
                        if (!temp.isAnnotationPresent(Excel.class)) {
                            continue;
                        }
                        for (int i = 0; i < headers.size(); i++) {
                            JSONObject header = (JSONObject) headers.get(i);
                            if (temp.getName().equals(header.get("key"))) {
                                flag = true;
                                break out;
                            }
                        }
                    }
                    if (flag) {
                        dynamicChangeAndSaveSourceAnnotation(headers, collectionClazz, excelMap);
                    } else {
                        ExcelCollection annotation = field.getAnnotation(ExcelCollection.class);
                        excelMap.put(field.getName(), annotation.name());
                        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
                        changeAnnotationValue(handler, field.getName() + "_ignore");
                    }
                }
            // @ExcelEntity注解
            } else if (field.isAnnotationPresent(ExcelEntity.class)) {
                Class entityClazz = field.getType();
                dynamicChangeAndSaveSourceAnnotation(headers, entityClazz, excelMap);
            }
        }
        return excelMap;
    }

    // 改变注解属性值,抽取的公共方法

    private static void changeAnnotationValue(InvocationHandler handler, String propertyValue) {
        try {
            Field field = handler.getClass().getDeclaredField("memberValues");
            field.setAccessible(true);
            Map<String, Object> memberValues = (Map<String, Object>) field.get(handler);
            memberValues.put("name", propertyValue);
        } catch (Exception e) {
            logger.error("替换注解属性值出错!", e);
        }
    }

2、用easypoi导出excel二进制文件

        这里就不上代码了,可以参考easypoi教程http://easypoi.mydoc.io/#text_186900

3、生成excel后,恢复Model每个字段上@Excel注解name属性原值

/**
     * 递归恢复@Excel原始的name属性
     */
    public static void dynamicResetAnnotation(Class clazz, Map<String, String> excelMap) {
        if (excelMap.isEmpty()) {
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                if (field.isAnnotationPresent(Excel.class)) {
                    if (excelMap.containsKey(field.getName())) {
                        Excel annotation = field.getAnnotation(Excel.class);
                        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
                        String sourceName = excelMap.get(field.getName());
                        changeAnnotationValue(handler, sourceName);
                    }
                } else if (field.isAnnotationPresent(ExcelCollection.class) && field.getType().isAssignableFrom(List.class)) {
                    // ExcelCollection修改过,才进行复原
                    if (excelMap.containsKey(field.getName())) {
                        ExcelCollection annotation = field.getAnnotation(ExcelCollection.class);
                        InvocationHandler handler = Proxy.getInvocationHandler(annotation);
                        String sourceName = excelMap.get(field.getName());
                        changeAnnotationValue(handler, sourceName);
                    // ExcelCollection未修改过,递归复原泛型字段
                    } else {
                        Type type = field.getGenericType();
                        if (type instanceof ParameterizedType) {
                            ParameterizedType pt = (ParameterizedType) type;
                            Class collectionClazz = (Class) pt.getActualTypeArguments()[0];
                            dynamicResetAnnotation(collectionClazz, excelMap);
                        }
                    }
                } else if (field.isAnnotationPresent(ExcelEntity.class)) {
                    Class entityClazz = field.getType();
                    dynamicResetAnnotation(entityClazz, excelMap);
                }
            }
        } catch (Exception e) {
            logger.error("解析动态表头,恢复注解属性值出错!", e);
        }
    }

 

其他代码,包括easypoi生成excel、response写出文件,网上很多案例,就不多说了。

经测试,这个简单处理方法可行,@Excel、@ExcelCollection、@ExcelEntity均有考虑到,可以兼容Model嵌套的问题。

代码刚刚撸完,还没详细检查,可能有不完善的地方,大家发现,请留言指点!

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Easypoi中,可以使用excel模板来导出自定义变量。首先,我们需要创建一个基于模板的excel文件,可以使用Excel软件进行创建。在模板文件中,我们可以在需要插入变量的位置,使用特定的占位符,例如${variable}来标记变量的位置。 接下来,在Java代码中使用Easypoi进行数据的导出。首先,我们需要创建一个Workbook对象,可以使用PoiExcelUtil.createWorkbook方法来创建。然后,我们可以使用PoiBaseView.render方法,将模板文件和需要导出的数据进行渲染。 在渲染时,我们可以使用PoiBaseView.putVar方法,将需要替换的变量名和对应的值进行绑定。例如,可以使用putVar("variable", value)方法来绑定变量名和对应的值。 最后,我们可以使用PoiBaseView.export方法,将渲染后的Workbook对象导出为Excel文件,可以是本地文件或者输出流。 在导出过程中,Easypoi会自动根据模板中的占位符替换为我们传入的变量值,实现了自定义变量的导出功能。 需要注意的是,模板文件中的占位符名称必须与Java代码中的变量名保持一致,这样才能正确地进行替换。否则,渲染后的Excel文件中将会保留占位符,而非替换为变量值。 综上所述,Easypoiexcel模板功能可以方便地导出包含自定义变量的Excel文件,通过在模板中使用特定的占位符,并在Java代码中进行渲染和替换即可实现。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值