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 原实体类字段名和@Excel注解name属性值的映射关系<字段名,@Excel注解name属性值>
     */
    public static  Map dynamicChangeAndSaveSourceAnnotation(JSONArray headers, Class clazz, Map 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 memberValues = (Map) 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 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嵌套的问题。

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

你可能感兴趣的:(java)