Java导出写入固定Excel模板数据

目录

介绍

引入easyExcel

sheet页属性单值写入

sheet页列表数据写入

数据占位写入

非占位写入


介绍

有时数据导入导出时,有些excel是固定好的标题数据,也就是固定的excel模板数据,此时让我们进行数据的写出,按照固定配置的标题数据进行导出excel

比如下面的样式,治理信息页面的

可以看到治理信息sheet页是固定好的表头模板,其他sheet页是集合列表形式,也是固定好表头的;此时可以使用阿里的easyExcel来简单实现excel固定模板数据的导出

引入easyExcel

引入maven依赖

java 复制代码
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>easyexcel</artifactId>
   <version>4.0.3</version>
</dependency>

sheet页属性单值写入

数据库表和java实体类需要和excel表sheet数据进行建模对应,这里简单介绍下实体类的对应,数据库建表,service,mapper层不再介绍,博主使用的是mybatis-plus来和数据库进行查询交互

sheet页属性单值,可以看到博主这里列举的例子该sheet页的每个属性值都是单一数据,和java中的实体属性一一对应,此时使用数据写入时,excel的对应位置需要直接写入对应实体属性值进行占位,并且使用 {} 进行包裹,:

模板excel写好占位属性值后,将对应excel放入java工程目录的rescourse目录下

书写方法查询数据,然后进行数据写入

java 复制代码
public void exportGovernanceReport(String orgId, HttpServletResponse response) {
        QyxxStandardGovernanceReportDto dto = queryExportGovernanceReport(orgId);
        if(dto == null){
            log.warn("导出数据或列不存在");
            return;
        }
        // 方法1: 使用 Spring 的 ClassPathResource 来定位文件
        ClassPathResource resource = new ClassPathResource("excelTemp/governanceReport.xlsx");

        try (
                // 1. 读取模板到内存
                ByteArrayOutputStream templateOut = new ByteArrayOutputStream();
        ) {
            // 先加载模板
            EasyExcel.write(templateOut)
                    .withTemplate(resource.getInputStream())
                    .build()
                    .finish();

            byte[] templateBytes = templateOut.toByteArray();
            try (
                    // 2. 基于模板进行填充
                    ByteArrayInputStream templateInput = new ByteArrayInputStream(templateBytes);
                    ByteArrayOutputStream output = new ByteArrayOutputStream()
            ) {
                ExcelWriterBuilder writerBuilder = EasyExcel.write(output).withTemplate(templateInput);


                // 构建 writer 和 sheet
                ExcelWriter writer = writerBuilder.build();
                WriteSheet sheet = EasyExcel.writerSheet("治理信息").build();

                // ✅ EasyExcel 4.x 正确填充方式:直接传 dto 对象
                // 前提:Excel 模板中写的是 {orgFullName},而不是 {data.orgFullName}
                writer.fill(dto, sheet);
                writer.finish();

                // 3. 输出到 response
                byte[] finalBytes = output.toByteArray();
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setCharacterEncoding("utf-8");
                String fileName = URLEncoder.encode("治理报告", "UTF-8").replaceAll("\\+", "%20");
                response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
                response.setContentLength(finalBytes.length);

                try (ServletOutputStream servletOutputStream = response.getOutputStream()) {
                    servletOutputStream.write(finalBytes);
                    servletOutputStream.flush();
                }

            } catch (IOException e) {
                log.error("导出失败:写入响应流异常", e);
                throw new ApiException("导出失败:请联系管理员");
            }

        } catch (Exception e) {
            log.error("导出治理报告失败: {}", CommonUtil.analysisExceptionMessage(e), e);
            throw new ApiException("导出失败:请联系管理员");
        }
    }

注意这里的方法,这里是博主的一个查询方法,这里可以忽略,因为无论什么样的场景这里都是会有查询数据的地方,这里在用时可以灵活实现自己的逻辑,反正就是到这一步时,dto里就已经查询到数据了,查询到数据后,下面的步骤才进入到写入的逻辑

打个断点看下这里查询的数据

可以看到dto这里已经查询到数据了

后续进行写入

先拿取放在rescourse目录下的excle模板文件

使用apipost测试运行

可以看到都出的excel文件,指定占位的地方已被替换为对应数据的值

sheet页列表数据写入

前面介绍的是sheet页的单值属性写入,在有的数据里是以列表形式的数据绑定,一行就是一条数据,

比如下面的示例

此时的数据绑定有两种方法可以进行数据写入

数据占位写入

第一种写法还是按照{} 数据占位的写法,但是这里的占位写法需要调整下

还是dto实体里,要定义好列表sheet页的实体属性集合

高管信息的就不再展示,同理,excel里占位 需要加上 前缀进行占位 {xx.属性值}

下面上代码

java 复制代码
QyxxStandardGovernanceReportDto dto = queryExportGovernanceReport(orgId);
        if(dto == null){
            log.warn("导出数据或列不存在");
            return;
        }
        // 方法1: 使用 Spring 的 ClassPathResource 来定位文件
        ClassPathResource resource = new ClassPathResource("excelTemp/governanceReport.xlsx");

        try (
                // 1. 读取模板到内存
                ByteArrayOutputStream templateOut = new ByteArrayOutputStream();
        ) {
            // 先加载模板
            EasyExcel.write(templateOut)
                    .withTemplate(resource.getInputStream())
                    .build()
                    .finish();

            byte[] templateBytes = templateOut.toByteArray();
            try (
                    // 2. 基于模板进行填充
                    ByteArrayInputStream templateInput = new ByteArrayInputStream(templateBytes);
                    ByteArrayOutputStream output = new ByteArrayOutputStream()
            ) {
                ExcelWriterBuilder writerBuilder = EasyExcel.write(output).withTemplate(templateInput);


                // 构建 writer 和 sheet
                ExcelWriter writer = writerBuilder.build();
                WriteSheet sheet = EasyExcel.writerSheet("治理信息").build();

                // ✅ EasyExcel 4.x 正确填充方式:直接传 dto 对象
                // 前提:Excel 模板中写的是 {orgFullName},而不是 {data.orgFullName}
                writer.fill(dto, sheet);
                Map<String, List<?>> dataMap = new HashMap<>();
                dataMap.put("席位归属分布", dto.getBoardSeatDistributions());
                dataMap.put("董事信息", dto.getDirectorInfos());
                // 后续还有其他多个sheet页,还可以接着添加
                // ========== 循环填充每个 sheet ==========
                FillConfig verticalConfig = FillConfig.builder().direction(WriteDirectionEnum.VERTICAL).build();

                for (Map.Entry<String, List<?>> entry : dataMap.entrySet()) {
                    String sheetName = entry.getKey();
                    List<?> dataList = entry.getValue();

                    if (CollectionUtils.isEmpty(dataList)) {
                        log.warn("Sheet [{}] 数据为空,跳过填充", sheetName);
                        continue;
                    }
                    WriteSheet sheetNameSheet = EasyExcel.writerSheet(sheetName).build();
                    // 使用 FillWrapper,统一用 "data" 作为占位符前缀
                    writer.fill(new FillWrapper("data", dataList), verticalConfig, sheetNameSheet);
                }

                // 3. 输出到 response
                byte[] finalBytes = output.toByteArray();
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setCharacterEncoding("utf-8");
                String fileName = URLEncoder.encode("治理报告", "UTF-8").replaceAll("\\+", "%20");
                response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
                response.setContentLength(finalBytes.length);

                try (ServletOutputStream servletOutputStream = response.getOutputStream()) {
                    servletOutputStream.write(finalBytes);
                    servletOutputStream.flush();
                }

            } catch (IOException e) {
                log.error("导出失败:写入响应流异常", e);
                throw new ApiException("导出失败:请联系管理员");
            }

        } catch (Exception e) {
            log.error("导出治理报告失败: {}", CommonUtil.analysisExceptionMessage(e), e);
            throw new ApiException("导出失败:请联系管理员");
        }
    }

这里由于是拿取对应名字的sheet页,所以将sheet页名字作为map的key,对应sheet页要传入的集合数据作为map的value,循环map进行写入excel数据

测试接口查看文件:

可以看到数据已经渲染上

非占位写入

excel模板里如果不想写入占位符进行占位绑定,需要在java对应实体类中使用

@ExcelProperty("对应excel列名")

代码调整:

java 复制代码
public void exportGovernanceReport(String orgId, HttpServletResponse response) {
        QyxxStandardGovernanceReportDto dto = queryExportGovernanceReport(orgId);
        if(dto == null){
            log.warn("导出数据或列不存在");
            return;
        }


        // 方法1: 使用 Spring 的 ClassPathResource 来定位文件
        ClassPathResource resource = new ClassPathResource("excelTemp/governanceReports.xlsx");
        System.out.println(resource.getFilename());
        System.out.println(resource.exists());
        System.out.println(resource.getPath());
        try (
                // 1. 读取模板到内存
                ByteArrayOutputStream templateOut = new ByteArrayOutputStream();
        ) {
            // 先加载模板
            EasyExcel.write(templateOut)
                    .withTemplate(resource.getInputStream())
                    .build()
                    .finish();

            byte[] templateBytes = templateOut.toByteArray();

            try (
                    // 2. 基于模板进行填充
                    ByteArrayInputStream templateInput = new ByteArrayInputStream(templateBytes);
                    ByteArrayOutputStream output = new ByteArrayOutputStream()
            ) {
                ExcelWriterBuilder writerBuilder = EasyExcel.write(output).withTemplate(templateInput);


                // 构建 writer 和 sheet
                ExcelWriter writer = writerBuilder.build();
                WriteSheet sheet = EasyExcel.writerSheet("治理信息").build();

                // ✅ EasyExcel 4.x 正确填充方式:直接传 dto 对象
                // 前提:Excel 模板中写的是 {orgFullName},而不是 {data.orgFullName}
                writer.fill(dto, sheet);

                //// ========== ✅ Sheet2: 席位归属分布(集合数据)==========
                if (CollectionUtils.isNotEmpty(dto.getBoardSeatDistributions())) {
                    WriteSheet sheet2 = EasyExcel.writerSheet("席位归属分布")
                            .build();
                    writer.write(dto.getBoardSeatDistributions(), sheet2);
                } else {
                    System.out.println("【警告】boardSeatDistributions 为空");
                }

                // ========== ✅ Sheet2: 席位归属分布(集合数据)==========

                writer.finish();

                // 3. 输出到 response
                byte[] finalBytes = output.toByteArray();
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setCharacterEncoding("utf-8");
                String fileName = URLEncoder.encode("治理报告", "UTF-8").replaceAll("\\+", "%20");
                response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
                response.setContentLength(finalBytes.length);

                try (ServletOutputStream servletOutputStream = response.getOutputStream()) {
                    servletOutputStream.write(finalBytes);
                    servletOutputStream.flush();
                }

            } catch (IOException e) {
                log.error("导出失败:写入响应流异常", e);
                throw new ApiException("导出失败:请联系管理员");
            }

        } catch (Exception e) {
            log.error("导出治理报告失败: {}", CommonUtil.analysisExceptionMessage(e), e);
            throw new ApiException("导出失败:请联系管理员");
        }
    }

这里传入对应的实体数据集合

查看结果

可以看到数据正确渲染,但是有一个问题,就是渲染了多余的数据,实体字段里没有配置@ExcelProperty注解的字段数据也被渲染了,此时如果哪些字段不想被渲染需要加上@ExcelIgnore

即可

相关推荐
摇滚侠3 小时前
Spring Boot3零基础教程,KafkaTemplate 发送消息,笔记77
java·spring boot·笔记·后端·kafka
fat house cat_6 小时前
【netty】基于主从Reactor多线程模型|如何解决粘包拆包问题|零拷贝
java·服务器·网络·netty
青云交6 小时前
Java 大视界 -- Java 大数据在智能教育学习社区互动模式创新与用户活跃度提升中的应用(426)
java·大数据·学习·flink 实时计算·智能教育社区·互动模式创新·用户活跃度
神奇的海马体6 小时前
Tomcat隐藏版本号
java·tomcat
拜见老天師6 小时前
使用mybatis-plus,实现将排序时,字段值为NULL的数据排在最后
java·mybatis
应茶茶7 小时前
C++11 核心新特性:从语法重构到工程化实践
java·开发语言·c++
Reggie_L7 小时前
RabbitMQ -- 高级特性
java·rabbitmq·java-rabbitmq
lang201509289 小时前
Spring空安全指南:告别空指针异常
java·安全·spring