SpringBoot实现excel生成并且通过邮件发送的步骤详解

 更新时间:2023年10月31日 09:38:34   作者:java大能虫  
实际开发中,特别是在B端产品的开发中,我们经常会遇到导出excel的功能,更进阶一点的需要我们定期生成统计报表,然后通过邮箱发送给指定的人员, 今天要带大家来实现的就是excel生成并通过邮件发送,需要的朋友可以参考下

1. 开发环境

以下演示jdk选用1.8版本。springboot采用2.3.7.RELEASE版本。

excel生成通过alibaba的EasyExcel组件来实现,采用最新的稳定版本3.1.1

2. 思路

我们的核心实现分成两步: 1、生成一个excel 2、将excel作为附件添加到邮件中进行发送

于是基于此思路,我们结合EasyExcel提供的write方法来生成excel文件,该excel文件在邮件发送完成后需要删除;然后通过之前我们讲解过的邮件发送工具类来实现邮件发送

3. 实操

1、书写邮件发送工具类,其实现参考上述博文

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

3、实现easyExcel的数据监听类

/**
 * @author benjamin_5
 * @Description 数据监听类
 * @date 2022/10/5
 */
@EqualsAndHashCode(callSuper = true)
public class DataListener<T> extends AnalysisEventListener<T> {

    /**
     * 缓存数据列表
     */
    private final List<T> dataList = new ArrayList<>();

    @Override
    public void invoke(T data, AnalysisContext context) {
        dataList.add(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {

    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        this.invokeHeadMap(ConverterUtils.convertToStringMap(headMap,context),context);
    }

    public List<T> getDataList() {
        return dataList;
    }

}

4、创建生成excel文件的工具类ExcelUtil

public class ExcelUtil {

    /**
     * 生成excel文件
     * @param fileName excel文件路径
     * @param dataList 数据列表
     * @param clazz 导出对象类
     * @param <T>
     * @return
     * @throws IOException
     */
    public static <T> File generateExcel(String fileName, List<T> dataList, Class<T> clazz) throws IOException {
        // 生成文件
        File excel = new File(fileName);
        // excel写入
        EasyExcel.write(excel,clazz).sheet(0).doWrite(dataList);
        return excel;
    }
}

5、创建导出数据的实体类

/**
 * @author benjamin_5
 * @Description
 * @date 2022/10/5
 */
@Data
public class CustomerData implements Serializable {

    @ExcelProperty(value = "客户名称")
    @ColumnWidth(value = 25)
    private String name;

    @ExcelProperty(value = "客户地址")
    @ColumnWidth(value = 50)
    private String address;

    @ExcelProperty(value = "联系电话")
    private String phone;

    @ExcelProperty(value = "金额")
    private BigDecimal amount;

    @ExcelProperty(value = "注册日期")
    @DateTimeFormat(value = "yyyy-MM-dd")
    private Date createDate;
}

6、实现excel数据生成及邮件发送的接口。为了演示方便我直接在controller中书写了,实际工作中应该把生成并发送的方法提取到工具类或者Service层中。

这里调用的是基于spring-boot-starter-mail实现的邮件发送工具类

@GetMapping("generateExcelAndSend")
    public void generateExcelAndSend(){
        List<CustomerData> dataList = new ArrayList<>();
        // 构造假数据
        for (int i = 0; i < 100; i++) {
            CustomerData data = new CustomerData();
            data.setName("客户"+i);
            data.setAddress("贵州省贵阳市观山湖区101号");
            data.setPhone("13889999999");
            data.setAmount(BigDecimal.valueOf(Math.random()*10000));
            data.setCreateDate(new Date());
            dataList.add(data);
        }
        // 获取资源文件存放路径,用于临时存放生成的excel文件
        String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
        // 文件名:采用UUID,防止多线程同时生成导致的文件重名
        String fileName = String.format("%s客户统计数据-%s.xlsx",path,UUID.randomUUID());
        try {
            // 生成excel文件
            File excel = ExcelUtil.generateExcel(fileName, dataList, CustomerData.class);
            // 发送邮件
            String content = "";
            String toMail = "wuhanxue5@sina.com";
            String ccMail = "wuhanxue5@163.com";
            emailSpringUtil.sendEmail("客户统计数据",content,true,"邮件提醒系统",
                    toMail,ccMail,null, Collections.singletonList(fileName));
            // 邮件发送完成后删除临时生成的excel文件
            excel.delete();
        } catch (IOException e) {
            logger.error(String.format("生成excel失败,原因:%s",e));
            e.printStackTrace();
        } catch (MessagingException e) {
            logger.error(String.format("邮件发送失败,原因:%s",e));
            e.printStackTrace();
        }
    }

7、同时因为我们的附件名称后面添加一个了个UUID,mime.mail中的参数splitlongparameters默认为 true,当附件名过长时,他会自动截取,就会导致我们接收到的附件格式变成.bin形式的。

要解决该问题就需要将其设置为false。于是我们创建一个启动执行类来单独设置

@Configuration
public class EmailToLongConfig {

    @PostConstruct
    private void init(){
        // 解决邮件附件名称太长会自动截取,导致附件变成.bin格式问题
        System.setProperty("mail.mime.splitlongparameters","false");
    }
}

当然我们也可以将System.setProperty("
mail.mime.splitlongparameters","false");放到邮件发送的方法中去。

8、启动项目,浏览器访问接口测试

http://localhost:8080/excel/generateExcelAndSend

可以看到上述邮件发送成功,excel附件也接收正常,查看附件内容也正常

优化

上述的实现,需要先创建一个文件然后又删除,不是很方便,我们可以采取直接用流输入输出

1、首先生成excel的方法调整为返回输出流

public static <T> ByteArrayOutputStream generateExcel(List<T> dataList, Class<T> clazz) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // excel写入
        EasyExcel.write(out,clazz).sheet(0).doWrite(dataList);
        return out;
    } 

2、其次发送邮件的方法调整为,直接接收输入流

public void sendEmail(String subject, String content, boolean contentIsHtml, String fromMailPersonalName,
                          String toMail, String ccMail, String bccMail, String fileName, InputStreamSource fileInput) throws MessagingException, UnsupportedEncodingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(mailProperties.getUsername(), fromMailPersonalName);
        helper.setTo(toMail);
        if (!ObjectUtils.isEmpty(ccMail)) {
            helper.setCc(ccMail);
        }
        if (!ObjectUtils.isEmpty(bccMail)) {
            helper.setBcc(bccMail);
        }
        helper.setSubject(subject);
        helper.setText(content, contentIsHtml);
        // 设置附件(注意这里的fileName必须是服务器本地文件名,不能是远程文件链接)
        if (fileInput != null) {
            helper.addAttachment(fileName, fileInput);
        }
        javaMailSender.send(message);
    }

3、主方法调整:不生成文件,而是通过流来传输

@GetMapping("generateExcelAndSend2")
    public void generateExcelAndSend2() throws IOException {
        long start = System.currentTimeMillis();
        List<CustomerData> dataList = new ArrayList<>();
        // 构造假数据
        for (int i = 0; i < 100; i++) {
            CustomerData data = new CustomerData();
            data.setName("客户"+i);
            data.setAddress("贵州省贵阳市观山湖区101号");
            data.setPhone("13889999999");
            data.setAmount(BigDecimal.valueOf(Math.random()*10000));
            data.setCreateDate(new Date());
            dataList.add(data);
        }
        // 获取资源文件存放路径,用于临时存放生成的excel文件
        String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
        // 文件名:采用UUID,防止多线程同时生成导致的文件重名
        String fileName = String.format("%s客户统计数据-%s.xlsx",path,UUID.randomUUID());
        ByteArrayOutputStream out = null;
        try {
            // 生成excel文件
            out = ExcelUtil.generateExcel(dataList, CustomerData.class);
            // 发送邮件
            String content = "客户统计数据如附件所示";
            String toMail = "wuhanxue5@sina.com";
            String ccMail = "wuhanxue5@163.com";
            emailSpringUtil.sendEmail("客户统计数据",content,false,"邮件提醒系统",
                    toMail,ccMail,null, fileName, new ByteArrayResource(out.toByteArray()));
        } catch (IOException e) {
            logger.error(String.format("生成excel失败,原因:%s",e));
            e.printStackTrace();
        } catch (MessagingException e) {
            logger.error(String.format("邮件发送失败,原因:%s",e));
            e.printStackTrace();
        }finally {
            if(out != null){
                out.close();
            }
            long end = System.currentTimeMillis();
            System.out.println("耗时:" + (end - start));
        }
    } 

4、最终测试下来,第二种方法要比之前方法快600ms左右

邮件正文中直接显示表格数据

有时候我们的统计数据不是很多,会更希望我们直接在邮件中展示表格数据,而不用再单独下载附件查看,这就需要用到HTML格式的邮件正文的实现

比较简单的实现就是循环数据集合,通过字符串拼接生成html的字符串。因为实现比较简单,这里就仅提供思路,如果有不清楚的同学可以留言提问。后续我们单独抽离成一个组件来实现生成html字符串的功能

总结

excel的生成以及邮件的发送,都应该尽可能的提取为工具类,如果实现的功能更多的更需要提取的单独的服务,通过pom依赖引入,更大化的实现方法的通用,和业务代码与通用代码之间的解耦。

以上就是SpringBoot实现excel生成并且通过邮件发送的步骤详解的详细内容,更多关于SpringBoot实现excel生成的资料请关注脚本之家其它相关文章!

相关文章

  • JUnit 5中扩展模型的深入理解

    JUnit 5中扩展模型的深入理解

    几乎所有的Java 开发人员都会使用JUnit 来做测试,但其实很多自动化测试人员也会使用Junit 。下面这篇文章主要给大家介绍了关于JUnit 5中扩展模型的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-08-08
  • Java面向对象基础知识之抽象类和接口

    Java面向对象基础知识之抽象类和接口

    这篇文章主要介绍了Java面向对象的抽象类和接口,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-11-11
  • Spring如何利用@Value注解读取yml中的map配置

    Spring如何利用@Value注解读取yml中的map配置

    这篇文章主要介绍了Spring如何利用@Value注解读取yml中的map配置,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • PageHelper引发的幽灵数据问题解析

    PageHelper引发的幽灵数据问题解析

    这篇文章主要为大家介绍了PageHelper引发的幽灵数据问题解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • spring jdbctemplate的用法小结

    spring jdbctemplate的用法小结

    jdbcTemplate是spring框架中提供的一个对象,是对原始繁杂的jdbc 对象的简单封装,本文通过实例代码介绍spring jdbctemplate的用法小结,需要的朋友可以参考下
    2023-04-04
  • java迭代子模式详解

    java迭代子模式详解

    这篇文章主要为大家详细介绍了java迭代子模式的相关资料,需要的朋友可以参考下
    2016-02-02
  • Java 手动解析不带引号的JSON字符串的操作

    Java 手动解析不带引号的JSON字符串的操作

    这篇文章主要介绍了Java 手动解析不带引号的JSON字符串的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • 如何使用Java读取PPT文本和图片

    如何使用Java读取PPT文本和图片

    这篇文章主要介绍了如何使用Java读取PPT文本和图片,本篇文章将介绍通过Java程序来读取PPT幻灯片中的文本及图片的方法。读取图片时,可读取文档中的所有图片,也可以读取指定幻灯片当中的图片,需要的朋友可以参考下
    2019-07-07
  • Java设计模式之迭代器模式

    Java设计模式之迭代器模式

    这篇文章介绍了Java设计模式之迭代器模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • Java程序员必须知道的5个JVM命令行标志

    Java程序员必须知道的5个JVM命令行标志

    这篇文章主要介绍了每个Java程序员必须知道的5个JVM命令行标志,需要的朋友可以参考下
    2015-03-03

最新评论