EasyPoi实现动态列的导入导出(固定列+自定义列)-横向(动态行头)+纵向-java

文章介绍了如何使用EasyPoi库在Java项目中实现纵向和横向动态列的Excel导入导出,包括使用@Excel注解、@ExcelCollection、Java反射获取参数以及ObjectValueUtils工具类处理不同类型的数据转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导入jar包

		<!-- 导入导出的工具包,可以完成Excel导出,导入,Word的导出,Excel的导出功能 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.3.0</version>
        </dependency>

        <!--耦合了spring-mvc 基于AbstractView,极大的简化spring-mvc下的导出功能 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.3.0</version>
        </dependency>

        <!-- 基础注解包,作用与实体对象上,拆分后方便maven多工程的依赖管理 -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.3.0</version>
        </dependency>

easyPoi开发文档: easyPoi开发文档

纵向导入导出:

项目需要导入导出数据,包含基本信息和规格参数List信息,最初采用easyPoi的注解导出,实现效果如下:

其中基本信息固定, 但产品尺寸信息List根据所属平台不同数量及名称不定

注解纵向合并实现效果

实体类

TbInstallProductVo对象纵向合并所用实体类注解:

注解实现纵向合并-TbInstallProductVo对象
在这里插入图片描述

除了@Excel注解, 主要使用了@ExcelCollection来实现导出集合的效果, 具体实现方法可以去官网查看

在这里插入图片描述

导出

 				//设置导出参数,获得工作簿,response返回
        ExportParams exportParams = new ExportParams("完美安装产品信息", "产品信息");
        Workbook wb = ExcelExportUtil.exportExcel(exportParams, TbInstallProductVo.class, list);
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("导出完美安装产品信息" + System.currentTimeMillis(), "UTF-8"));
            wb.write(response.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        IOUtils.closeQuietly(wb);

导入

public AjaxResult importData(MultipartFile file, Boolean updateSupport) {
		//设置导入参数,
        ImportParams importParams = new ImportParams();
        //表格标题行数,默认0
        importParams.setTitleRows(1);
        //表头行数,默认1
        importParams.setHeadRows(2);
        List<TbInstallProductVo> productVoList;
        try {
            productVoList = ExcelImportUtil.importExcel(file.getInputStream(), TbInstallProductVo.class, importParams);
        } catch (Exception e) {
            throw new RuntimeException("读取excel数据异常null");
        }
        if (StringUtils.isNotEmpty(productVoList)) {
            //导入信息
            return importProductInfoAndParamInfo(updateSupport, productVoList);
        } else {
            return AjaxResult.error("导入数据不能为空!");
        }
    }

横向动态列导入导出(行头动态生成)

现在需要按平台分类将产品规格信息的动态List的参数名称横置为列名,参数值为数据,形成不同导入导出模板,,必须采用动态列进行导入导出,实现效果如下:
在这里插入图片描述

解决方案

Java反射机制根据@Excel注解获取需要的参数+动态拼装ExcelExportEntity实现List动态行头

其中:基于List 的导出,ExcelExportEntity是注解经过处理翻译成的实体类,两者几乎是一对的,所以如果我们要动态自定义导出列,我们只要动态拼装ExcelExportEntity就可以实现不固定参数, 由于行头的名称也是数据库读取内容, 因此需要用java反射获取参数名称字段的内容作为行头

模板类对象
在这里插入图片描述

导出

    public void export(HttpServletResponse response, Long[] ids) {
        try {
        	//业务数据查询
            List<TbInstallProductVo> productVoList = tbInstallProductInfoService.selectTbInstallProductInfoListForExportV1(ids);
            List<Map<String, Object>> dataList = new ArrayList<>();
            //数据转为List<map>导出
            if (StringUtils.isNotEmpty(productVoList)) {
                for (TbInstallProductVo productVo : productVoList) {
                	//将相同模板的数据以<名称,数据>的格式转为map,以数据和行头匹配
                    dataList.add(getExportDataMap(productVo));
                }
            }
            //构建该模板导出的行头
            List<ExcelExportEntity> excelExportEntities = getExcelExportEntityList();
            //数据导出
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("产品信息列表", "用户导入"), excelExportEntities, dataList);
            downLoadExcel("导出完美安装产品信息" + System.currentTimeMillis(), response, workbook);
        } catch (Exception e) {
            throw new ServiceException("导出失败" + e);
        }
    }

根据类中注解通过反射构建行头

    private List<ExcelExportEntity> getExcelExportEntityList() throws Exception {
        Class<TbInstallProductVo> templateClass = TbInstallProductVo.class;
        //获取所有Excel注解的字段
        List<Field> fields = FieldUtils.getFieldsListWithAnnotation(templateClass, Excel.class);
        //获取参数值字段的注解
        Field parmaFiled = TbInstallParamInfoVo.class.getDeclaredField("paramName");
        List<ExcelExportEntity> excelExportEntities = new ArrayList<>();

        //获取基本信息导出-基本信息以Excel注解值为行头
        //@ExcelTarget 这个是作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理
        ExcelTarget etarget = (ExcelTarget) templateClass.getAnnotation(ExcelTarget.class);
        String targetId = etarget == null ? null : etarget.value();
        if (StringUtils.isNotEmpty(fields)) {
        			//循环带Excel注解的字段,获取Excel注解的name作为行头----固定的基本信息用此方式获取行头名称
            for (Field field : fields) {
                if (field.getAnnotation(Excel.class) != null) {
                    Excel excel = (Excel) field.getAnnotation(Excel.class);
                    String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, (String) null);
                    if (StringUtils.isNotBlank(name)) {
                    	//构建ExcelExportEntity
                        ExcelExportEntity excelExportEntity = this.createExcelExportEntity(field, targetId, templateClass, null, (ExcelEntity) null);
                        excelExportEntity.setKey(name);
                        excelExportEntities.add(excelExportEntity);
                    }
                }
            }
        }

        //根据模板动态加入需要导出的字段---动态列,行头名为数据paramName值
        //获取该模板的数据(业务)
        TbInstallTemplateParamInfo temParamParmas = new TbInstallTemplateParamInfo();
        temParamParmas.setTemplateId(-1L);
        List<TbInstallTemplateParamInfo> templateParamInfoList = tbInstallTemplateParamInfoMapper.selectTbInstallTemplateParamInfoList(temParamParmas);
        if (StringUtils.isNotEmpty(templateParamInfoList)) {
        	//循环模板的参数
            for (int i = 0; i < templateParamInfoList.size(); i++) {
            //判断参数名称字段是否存在有效注解
                if (parmaFiled.getAnnotation(Excel.class) != null) {
                    Excel excel = (Excel) parmaFiled.getAnnotation(Excel.class);
                    String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, (String) null);
                    if (StringUtils.isNotBlank(name)) {
                    	//存在注解,设置行头名称为参数值,并设置导出排序
                        ExcelExportEntity excelExportEntity = this.createExcelExportEntity(parmaFiled, targetId, TbInstallParamInfoVo.class, null, (ExcelEntity) null);
                        excelExportEntity.setName(templateParamInfoList.get(i).getParamName());
                        excelExportEntity.setOrderNum(fields.size() + i);
                        excelExportEntity.setKey(templateParamInfoList.get(i).getParamName());
                        excelExportEntities.add(excelExportEntity);
                    }
                }
            }
        }
        //返回固定列+该模板动态列格式
        return excelExportEntities;
    }

业务数据转为List<map>

    private Map<String, Object> getExportDataMap(TbInstallProductVo productVo) throws Exception {
        Class<TbInstallProductVo> templateClass = TbInstallProductVo.class;
        List<Field> fields = FieldUtils.getFieldsListWithAnnotation(templateClass, Excel.class);
        //获取参数值字段的注解
        Field parmaFiled = TbInstallParamInfoVo.class.getDeclaredField("paramName");
        Map<String, Object> map = new HashMap<>();

        //获取基本信息的数据
        ExcelTarget etarget = (ExcelTarget) templateClass.getAnnotation(ExcelTarget.class);
        String targetId = etarget == null ? null : etarget.value();
        if (StringUtils.isNotEmpty(fields)) {
            for (Field field : fields) {
                if (field.getAnnotation(Excel.class) != null) {
                    Excel excel = (Excel) field.getAnnotation(Excel.class);
                    String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, (String) null);
                    if (StringUtils.isNotBlank(name)) {
                        field.setAccessible(true);
                        map.put(name, ObjectValueUtils.getValue(field.get(productVo), field.getType()));
                    }
                }
            }
        }
        //根据productVo生成参数数据存入map
        if (StringUtils.isNotEmpty(productVo.getParamInfoVoList())) {
            for (int i = 0; i < productVo.getParamInfoVoList().size(); i++) {
                if (parmaFiled.getAnnotation(Excel.class) != null) {
                    Excel excel = (Excel) parmaFiled.getAnnotation(Excel.class);
                    String name = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, (String) null);
                    if (StringUtils.isNotBlank(name)) {
                   	 	//<参数名称,参数值>
                        map.put(productVo.getParamInfoVoList().get(i).getParamName(), productVo.getParamInfoVoList().get(i).getParamValue());
                    }
                }
            }
        }
        return map;
    }

导入

    public AjaxResult importData(MultipartFile file, Boolean updateSupport) {

        Class<TbInstallProductVo> importClass = TbInstallProductVo.class;
        List<Map<String, Object>> mapList = null;
        List<TbInstallProductVo> productVoList = new ArrayList<>();
        try {
            ImportParams importParams = new ImportParams();
            importParams.setTitleRows(1);
            importParams.setHeadRows(1);
            //获取导入的数据,按<行头,内容>格式
            mapList = ExcelImportUtil.importExcel(file.getInputStream(), Map.class, importParams);
        } catch (Exception e) {
            throw new RuntimeException("读取excel数据异常" + e);
        }
        if (StringUtils.isEmpty(mapList)) {
            throw new ServiceException("导入产品数据为空!");
        }
        //获取固定参数
        List<Field> fields = FieldUtils.getFieldsListWithAnnotation(importClass, Excel.class);
        ExcelTarget etarget = (ExcelTarget) importClass.getAnnotation(ExcelTarget.class);
        String targetId = etarget == null ? null : etarget.value();
        //根据模板动态加入需要导出的字段
        TbInstallTemplateParamInfo temParamParmas = new TbInstallTemplateParamInfo();
        temParamParmas.setTemplateId(-1L);
        List<TbInstallTemplateParamInfo> templateParamInfoList = tbInstallTemplateParamInfoMapper.selectTbInstallTemplateParamInfoList(temParamParmas);
        try {
            for (Map<String, Object> map : mapList) {
                //构建导入数据List
                TbInstallProductVo productVo = importClass.newInstance();
                //构建固定字段-固定字段以注解为行头
                if (StringUtils.isNotEmpty(fields)) {
                    for (Field field : fields) {
                        if (field.getAnnotation(Excel.class) != null) {
                            Excel excel = (Excel) field.getAnnotation(Excel.class);
                            String excelName = PoiPublicUtil.getValueByTargetId(excel.name(), targetId, (String) null);
                            if (StringUtils.isNotBlank(excelName)) {
                                //设置允许访问,并强制类型转换解决解析类型冲突
                                field.setAccessible(true);
                  				//设置参数值为导入的数据
                                field.set(productVo, ObjectValueUtils.getValue(map.get(excelName), field.getType()));
                            }
                        }
                    }
                }
                //构建不定产品参数
                if (StringUtils.isNotEmpty(templateParamInfoList)) {
                    List<TbInstallParamInfoVo> paramInfoVoList = new ArrayList<>();
                    for (TbInstallTemplateParamInfo tempParamInfo : templateParamInfoList) {
                    //设置参数对象以构建自定义参数List
                        TbInstallParamInfoVo paramInfoVo = new TbInstallParamInfoVo();
                        paramInfoVo.setParamName(tempParamInfo.getParamName());
                        paramInfoVo.setParamCode(tempParamInfo.getParamCode());
                        paramInfoVo.setParamValue(String.valueOf(map.get(tempParamInfo.getParamName())));
                        paramInfoVoList.add(paramInfoVo);
                    }
                    productVo.setParamInfoVoList(paramInfoVoList);
                }
                productVoList.add(productVo);
            }
            log.info("完美安装导入的数据=={}", productVoList);
        } catch (Exception e) {
            throw new RuntimeException("数据解析错误,请检查数据内容" + e);
        }
        //导入数据-业务逻辑
        return tbInstallProductInfoServiceImpl.importProductInfoAndParamInfo(updateSupport, productVoList);
    }

参考: EasyPoi导入与导出_easypoi map格式导出_阿里巴巴P8资深技术专家的博客-优快云博客

补充ObjectValueUtils工具类

import java.text.ParseException;
import java.util.Date;

/**
 * @author Shenpeng
 * @Date 2023/11/27/16:21
 * @Description
 */
public class ObjectValueUtils {
    /**
     * description 根据类型class转换类型值
     *
     * @param value:
     * @param type:
     * @return :Object
     */
    public static Object getValue(Object value, Class type) throws ParseException {
        if (value != null) {

            if (type.isAssignableFrom(String.class))
                return toString(value);
            if (type.isAssignableFrom(Integer.class) || type.isAssignableFrom(int.class))
                return toInteger(value);
            if (type.isAssignableFrom(Long.class) || type.isAssignableFrom(long.class))
                return toLong(value);
            if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class))
                return toBoolean(value);
            if (type.isAssignableFrom(Double.class) || type.isAssignableFrom(double.class))
                return toDouble(value);
            if (type.isAssignableFrom(Date.class))
                return toDate(value);
            if (type.isAssignableFrom(String[].class))
                return toStringArray(value);
            if (type.isAssignableFrom(Integer[].class))
                return toIntegerArray(value);
        }
        return value;
    }

    private static String[] toStringArray(Object value) {
        return value.toString().split(",");
    }

    private static Integer[] toIntegerArray(Object value) {
        String[] stringArray = toStringArray(value);
        Integer[] intArray = new Integer[stringArray.length];
        for (int i = 0; i < stringArray.length; i++) {
            intArray[i] = Integer.parseInt(stringArray[i]);
        }
        return intArray;
    }

    private static Integer toInteger(Object value) {
        return Integer.parseInt(value.toString());
    }

    private static Long toLong(Object value) {
        return Long.parseLong(value.toString());
    }

    private static Double toDouble(Object value) {
        return Double.parseDouble(value.toString());
    }

    private static String toString(Object value) {
        return value.toString();
    }

    private static Date toDate(Object value) throws ParseException {
        return DateUtils.parseDate(value.toString(), DateUtils.YYYY_MM_DD);
    }

    private static Boolean toBoolean(Object value) {
        return Boolean.parseBoolean(value.toString());
    }
}

补充createExcelExportEntity方法

private ExcelExportEntity createExcelExportEntity(Field field, String targetId, Class<?> pojoClass,
                                                      List<Method> getMethods, ExcelEntity excelGroup)throws Exception {
        Excel excel = (Excel) field.getAnnotation(Excel.class);
        ExcelExportEntity excelEntity = new ExcelExportEntity();
        excelEntity.setType(excel.type());
        this.getExcelField(targetId, field, excelEntity, excel, pojoClass, excelGroup);
        if (getMethods != null) {
            List<Method> newMethods = new ArrayList();
            newMethods.addAll(getMethods);
            newMethods.add(excelEntity.getMethod());
            excelEntity.setMethods(newMethods);
        }
        return excelEntity;
    }
<think>好的,我现在需要帮助用户解决关于使用Easypoi导入复杂表头Excel的问题。首先,我要回顾用户提供的引用内容,特别是引用3和4,因为它们直接提到了复杂表头的处理。 用户的问题是如何实现复杂表头的导入,可能需要详细的步骤和示例代码。根据引用3,当复杂表头的子字段名称不同时,处理起来相对简单,不需要fixIndex。同时,使用@ExcelEntity注解可以映射复杂表头。引用4提到了ExcelImportUtil.importExcelMore方法,这可能用于处理一对多的导入情况。 接下来,我需要组织一个结构清晰的回答,分步骤说明实现过程。首先,创建实体类结构,确保正确使用@Excel注解和@ExcelEntity。然后,处理Excel数据,包括导入工具类的使用和结果处理。可能需要提供一个示例实体类,如MainHeader和SubHeader,并展示如何标注注解。 同时,需要指出注意事项,比如表头层级、名称一致性和异常处理。还要生成相关问题,可能涉及一对多导入、性能优化或异常处理技巧。 要确保所有数学表达式用$...$格式,但这个问题可能不涉及数学公式,所以可能不需要。回答要使用中文,引用适当的地方添加标识,比如引用3和4。 最后,检查是否符合系统级指令,特别是引用格式、代码块的使用和相关问题的生成。确保步骤清晰,示例代码正确,并且引用正确。</think>[用户可见层] # Easypoi复杂表头Excel导入实现步骤 ## 一、实体类配置(核心步骤) 1. 创建主表头实体类 ```java public class MainHeader { @Excel(name = "主表头名称/子表头1", width = 15) private String subHeader1; @Excel(name = "主表头名称/子表头2", width = 15) private String subHeader2; @ExcelEntity(name = "嵌套表头") private SubHeader subHeader; } ``` 2. 创建嵌套表头实体类(支持多层级) ```java public class SubHeader { @Excel(name = "二级表头/三级表头A", width = 20) private String level3A; @Excel(name = "二级表头/三级表头B", width = 20) private String level3B; } ``` > 通过`/`符号实现表头层级关系映射[^3] ## 二、Excel数据处理 ```java // 导入工具类使用 ExcelImportResult<MainHeader> result = ExcelImportUtil.importExcelMore( file.getInputStream(), MainHeader.class, new ImportParams() ); // 结果处理 if(result.isVerifyFail()){ result.getFailList().forEach(fail -> { System.out.println("失败行号:" + fail.getRowNum()); System.out.println("失败原因:" + fail.getFailMsg()); }); } List<MainHeader> successList = result.getList(); ``` ## 三、注意事项 1. 表头层级限制:最多支持3级表头(超过需要特殊处理) 2. 字段名称一致性:Excel表头内容必须与`@Excel`注解的name属性完全匹配 3. 异常处理:建议配合try-catch块处理格式错误 ```java try { // 导入代码 } catch (ExcelImportException e) { // 处理导入异常 } ```
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值