EasyExcel导出复杂表格到邮箱

简介: 自定义单元格合并策略、调用示例、工具类Bo、Excel 模板导出工具类、Vo配置、效果图、MailUtils、模板配置

EasyExcel导出复杂表格到邮箱

📔 千寻简笔记介绍

千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes,包含笔记源文件.md,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对你有帮助请帮我点一个Star~

更新:支持在线阅读文章,根据发布日期分类。

@[toc]

简介

在企业开发中,我们经常需要面对复杂的Excel导出,本文基于EasyExcel,自定义单元格合并策略,达到我们需要导出的效果。

邮箱配置可以参考主页的ruoyi-vue-plus 配置邮箱。

效果图

image-20230828185404792

版本

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.3.2</version>
        </dependency>

本文关键词

自定义单元格合并策略、调用示例、工具类Bo、Excel 模板导出工具类、Vo配置、效果图、MailUtils、模板配置

实现步骤

导出到邮箱

1 调用示例

  • 模板存放位置src/main/resources/templates 目录下。
  • templateName只传文件名:WorkSafetyReportTemplate.xlsx。
  • setMergeRowIndex:从哪一行开始合并0开始。
  • setUniqueColumnIndex:唯一标识列。
  • setMergeColumnIndex:需要合并的列。
  • setList:导出的数据。
  • MailUtils.sendText:使用ruoyi提供的MailUtils工具发送邮件。
    /***
     * 1 根据模板的生成对应excel文件
     * 2.使用ruoyi提供的MailUtils工具发送邮件,附件为生成的excel文件
     * **/
    public Boolean exportExcelToEmail(XXXX bo) {
   
   
        // 省略业务查询
        List<Xxxxxx> dataList = iRiskXxxxxxService.queryList();

        EasyExcelExportBo exportBo = new EasyExcelExportBo();
        exportBo.setTemplateName("WorkXxxxxTemplate.xlsx");
        exportBo.setMergeRowIndex(1);
        exportBo.setUniqueColumnIndex(1);
        exportBo.setMergeColumnIndex(Sets.newHashSet(1,2,3,11));
        exportBo.setList(dataList);
        // 返回文件路径
        String filePath = new EasyExcelExportUtil().export(exportBo);
        File file = new File(filePath);
        MailUtils.sendText(bo.getEmail(), "导出附件", "导出测试清单", file);
        return true;
    }

2 工具类Bo

  • 用于传输导出必要参数。

EasyExcelExportBo.java

import lombok.Data;
import java.util.List;
import java.util.Set;

/**
 * 导出必要参数
 * @author Star
 */
@Data
public class EasyExcelExportBo {
   
   
    /**
     * 唯一标识列(该列cell内容一定是全局唯一的,当cell的值相等时才能进行下一列合并)
     */
    private Integer uniqueColumnIndex;

    /**
     * 从哪一行开始合并,从0开始
     */
    private Integer mergeRowIndex;

    /**
     * 合并列编号,从0开始:需要合并的列
     */
    private Set<Integer> mergeColumnIndex;

    /**
     * 模板文件名
     *
     * 默认存放在src/main/resources/templates 目录下
     * templateName只传文件名:WorkSafetyReportTemplate.xlsx
     */
    private String templateName;

    /**
     * 导出数据Vo
     */
    private List<?> list;
}

3 Excel 模板导出工具类

  • 封装的Excel 模板导出工具类。

EasyExcelExportUtil.java

import cn.hutool.core.io.resource.ResourceUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.google.common.collect.Sets;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.papp.model.EasyExcelExportBo;
import com.ruoyi.project.domain.vo.RiskAssessmentVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

/**
 * Excel 模板导出工具类
 * @author Star
 */
public class EasyExcelExportUtil {
   
   
    private static Logger logger = LoggerFactory.getLogger(EasyExcelExportUtil.class);

    /**
     * 获取resources路径
     * @return
     */
    public static String getPath() {
   
   
        return EasyExcelExportUtil.class.getResource("/").getPath();
    }


    /**
     * 导出方法
     * @param paramet
     * @return 文件路径
     */
    public String export(EasyExcelExportBo paramet){
   
   
        if (paramet.getTemplateName() == null) {
   
   
            throw new ServiceException("请填写模板文件名");
        }
        if (paramet.getMergeRowIndex() == null) {
   
   
            throw new ServiceException("请填写合并行");
        }
        if (paramet.getUniqueColumnIndex() == null) {
   
   
            throw new ServiceException("请填写唯一标识列");
        }
        if (paramet.getList() == null || paramet.getList().size() == 0) {
   
   
            throw new ServiceException("请填充导出的数据");
        }
        if (paramet.getMergeColumnIndex() == null || paramet.getMergeColumnIndex().size() == 0) {
   
   
            throw new ServiceException("请输入需要合并的列");
        }

        //处理文件导出
        InputStream template = null;
        try {
   
   
            // 模板路径
            template = ResourceUtil.getStream("classpath:templates/"+paramet.getTemplateName());
            // 导出文件名称
            String fileName = getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx";

            //读取Excel
            ExcelWriter excelWriter = EasyExcel.write(fileName)
                .withTemplate(template)
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(new CellMergeStrategy(paramet.getUniqueColumnIndex(), paramet.getMergeRowIndex(), paramet.getMergeColumnIndex()))
                .build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            //list map 是查询并需导出的数据,并且里面的字段和excel需要导出的字段对应
            // 直接写入Excel数据
            excelWriter.fill(paramet.getList(), writeSheet);
            excelWriter.finish();
            logger.info("文件导出完毕,路径:"+fileName);
            return fileName;
        } catch (Exception e) {
   
   
            System.out.println(e.getMessage());
            throw new RuntimeException("导出文件错误");
        } finally {
   
   
            if (template != null) {
   
   
                try {
   
   
                    template.close();
                } catch (IOException e) {
   
   
                    e.printStackTrace();
                }
            }
        }
    }

}

4 自定义单元格合并策略(重点)

  • 自定义单元格合并策略,可以指定某一个唯一列uniqueColumnIndex做为合并的条件。

CellMergeStrategy.java

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import com.google.common.collect.Lists;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;
import java.util.Set;


/**
 * 自定义单元格合并策略:推荐使用
 * @author Star
 */
public class CellMergeStrategy extends AbstractMergeStrategy {
   
   

    /** 唯一标识列(该列cell内容一定是全局唯一的,当cell的值相等时才能进行下一列合并) */
    private Integer uniqueColumnIndex;
    /** 从哪一行开始合并 */
    private Integer mergeRowIndex = 0;
    /** 合并列编号,从0开始 */
    private List<Integer> mergeColumnIndex = Lists.newArrayList();

    private CellMergeStrategy() {
   
   
    }

    /**
     * @param uniqueColumnIndex 唯一标识列(该列cell内容一定是全局唯一的,当cell的值相等时才能进行下一列合并)
     * @param mergeRowIndex 从哪一行开始合并
     * @param mergeColumnIndex 合并列编号,从0开始:需要合并的列
     */
    public CellMergeStrategy(Integer uniqueColumnIndex, Integer mergeRowIndex, Set<Integer> mergeColumnIndex) {
   
   
        mergeColumnIndex.stream().forEach(item -> {
   
   
            this.mergeColumnIndex.add(item);
        });
        this.mergeColumnIndex.stream().sorted();

        if (null == uniqueColumnIndex) {
   
   
            this.uniqueColumnIndex = this.mergeColumnIndex.get(0);
        } else {
   
   
            this.uniqueColumnIndex = uniqueColumnIndex;
        }
        this.mergeRowIndex = mergeRowIndex;
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
   
   
        int curColIndex = cell.getColumnIndex();
        int curRowIndex = cell.getRowIndex();

        // 判断该列是否需要合并
        if (!mergeColumnIndex.contains(curColIndex)) {
   
   
            return;
        }

        if (curRowIndex > mergeRowIndex) {
   
   
            for (int i = 0; i < mergeColumnIndex.size(); i++) {
   
   
                if (curColIndex == mergeColumnIndex.get(i)) {
   
   
                    this.mergeRow(sheet, cell, curRowIndex, curColIndex, uniqueColumnIndex);
                    break;
                }
            }
        }
    }

    /**
     * 向上合并单元格
     * @param cell              当前单元格
     * @param rowIndex          当前行
     * @param colIndex          当前列
     * @param uniqueColIndex    唯一标识列(该列cell内容一定是全局唯一的,当cell的值相等时才能进行下一列合并)
     */
    private void mergeRow(Sheet sheet, Cell cell, int rowIndex, int colIndex, int uniqueColIndex) {
   
   
        Object curCellValue = getCellValue(cell);
        Object preCellValue = getCellValue(cell.getSheet().getRow(rowIndex - 1).getCell(colIndex));
        boolean cellEqual = preCellValue.equals(curCellValue);

        boolean baseCellEqual = true;
        if (colIndex >= uniqueColIndex) {
   
   
            Object baseCellValue = getCellValue(cell.getRow().getCell(uniqueColIndex));
            Object preBaseCellValue = getCellValue(cell.getSheet().getRow(rowIndex - 1).getCell(uniqueColIndex));
            baseCellEqual = baseCellValue.equals(preBaseCellValue);
        }

        /**
         * 合并条件
         * 1. 将当前单元格数据与上一个单元格数据比较,相同则执行合并逻辑
         * 2. 唯一标识列内容相同,才能进行下一列合并
         */
        if (!(cellEqual && baseCellEqual)) {
   
   
            return;
        }

        List<CellRangeAddress> mergeRegionList = sheet.getMergedRegions();
        boolean isMerged = false;
        for (int i = 0; i < mergeRegionList.size() && !isMerged; i++) {
   
   
            CellRangeAddress cellRange = mergeRegionList.get(i);
            // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
            if (cellRange.isInRange(rowIndex - 1, colIndex)) {
   
   
                sheet.removeMergedRegion(i);
                cellRange.setLastRow(rowIndex);
                sheet.addMergedRegion(cellRange);
                isMerged = true;
            }
        }
        // 若上一个单元格未被合并,则新增合并单元
        if (!isMerged) {
   
   
            CellRangeAddress cellRange = new CellRangeAddress(rowIndex - 1, rowIndex, colIndex, colIndex);
            sheet.addMergedRegion(cellRange);
        }
    }

    private Object getCellValue(Cell baseCell) {
   
   
        return CellType.STRING.equals(baseCell.getCellType()) ? baseCell.getStringCellValue() : baseCell.getNumericCellValue();
    }

}

5 Vo配置

  • 业务的数据

  • @ColumnWidth(100 / 8):列宽

  • @HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER):表头样式 居中
  • @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER,verticalAlignment = VerticalAlignmentEnum.CENTER):主题样式:水平垂直居中
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.annotation.write.style.HeadStyle;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * 测试使用
 */
@Data
@ColumnWidth(100 / 8)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER)//表头样式
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER,verticalAlignment = VerticalAlignmentEnum.CENTER)
public class WuYeDemo {
   
   
    @ExcelProperty(value = "属地")
    private String possession;

    @ExcelProperty(value = "风险点")
    private String riskPoint;

    @ExcelProperty(value = "风险点分类")
    private String riskPointClassification;

    @ExcelProperty(value = "风险场景")
    private String riskScenario;

    @ExcelProperty(value = "风险等级\n" +
        "(风险点)")
    private String riskGrade;
}

6 模板配置

  • {.字段名}:固定导出数据的位置

image-20230828190337018

7 效果图

文件导出路径:target/classes/templateWrite1693195524899.xlsx

image-20230828185404792

目录
相关文章
|
消息中间件 存储 负载均衡
ActiveMQ高可用集群部署方案
ActiveMQ是分布式系统中重要的组件,在生产环境中如何保证让ActiveMQ能够持续工作,同时还要使消息中间件服务保持可靠性和高效的处理性能。
7645 0
ActiveMQ高可用集群部署方案
|
10月前
|
机器学习/深度学习 弹性计算 应用服务中间件
阿里云服务器租赁价格标准整理汇总:一年/按月/按小时报价明细出炉
最新的阿里云服务器价格多少钱1年?阿里云服务器产品包含 ECS 云服务器、轻量应用服务器和 GPU 服务器,涵盖多种实例规格与配置,满足不同使用需求。今天小编总结一篇关于最新的阿里云服务器租赁价格标准整理汇总:包含一年/按月/按小时报价明细。
|
9月前
|
人工智能 监控 安全
每天150亿次攻击!钓鱼已“溢出”邮箱,全面攻陷你的工作聊天窗口
网络钓鱼已从“垃圾邮件”升级为全渠道攻击,借助AI伪造身份与话术,渗透微信、钉钉、Slack等协作工具。每天150亿次攻击背后,是信任机制的被滥用。企业需构建跨平台行为分析与多重验证机制,重塑安全防线。
307 2
|
调度 vr&ar 图形学
【干货】实时云渲染与本地渲染的技术对比
实时渲染分为本地渲染和云渲染两种模式。随着XR技术在建筑、教育、医疗等领域的广泛应用,越来越多企业选择云渲染以提升效率、降低成本并增强协同能力。本文对比分析了这两种渲染模式的优劣,并重点介绍了实时云渲染方案具备便捷性、高效资源调度、超低时延网络、数据安全、终端轻量化及跨系统运行等优势,满足多种XR应用场景需求。
946 13
|
存储 Linux
在Linux中,如何管理磁盘配额?
在Linux中,如何管理磁盘配额?
|
开发工具 git
idea结合git回到某个提交点
本文介绍了如何在IntelliJ IDEA中使用Git工具回退到之前的提交点,通过Git管理界面查看提交日志,选择特定提交并进行软重置、混合重置或硬重置以撤销后续的更改。
3063 1
idea结合git回到某个提交点
|
机器学习/深度学习 计算机视觉 异构计算
【保姆级教程|YOLOv8改进】【4】添加双层路由注意力机制:BiLevelRoutingAttention,性能和效率十分不错
【保姆级教程|YOLOv8改进】【4】添加双层路由注意力机制:BiLevelRoutingAttention,性能和效率十分不错
|
存储 人工智能 iOS开发
Adobe illustrator2023最新免费版下载及功能介绍AI2023
Adobe Illustrator (AI 2023)是Adobe在设计行业生产的最受欢迎的矢量图形软件之一,它已经成为行业标准之一。全球数百万设计师和艺术家正在使用Illustrator进行设计和艺术创作。Illustrator广泛应用于平面设计、标志设计、图标设计、书籍插图、包装设计、印刷、广告设计和插画设计。
3509 0
|
Ubuntu 异构计算
jetson系列--更换镜像源并安装jtop
更换镜像源并安装jtop
3732 0
jetson系列--更换镜像源并安装jtop
|
前端开发 网络架构 索引
NR PDCCH(二) SearchSpace
这篇开始介绍SearchSpace。CORESET 描述的是PDCCH 盲检资源的频域特性,SearchSpace 代表的是时域特性,具体的说就是有关时域周期和偏移、每周期内持续监测的时隙数和每个时隙内的监测的具体起始符号等,这些其实就是指示了CORESET的时域位置,两者结合构成确定的时频域资源用于PDCCH data (DCI)接收。

热门文章

最新文章