SpringBoot使用JavaMailSender实现发送邮件+Excel附件

 更新时间:2023年10月31日 09:37:59   作者:困知勉行1985  
项目审批完毕后,需要发送邮件通知相关人员,并且要附带数据库表生成的Excel表格,这就要求不光是邮件发送功能,还要临时生成Excel表格做为附件,本文详细介绍了SpringBoot如何使用JavaMailSender实现发送邮件+Excel附件,需要的朋友可以参考下

需求描述:项目审批完毕后,需要发送邮件通知相关人员,并且要附带数据库表生成的Excel表格,这就要求不光是邮件发送功能,还要临时生成Excel表格做为附件

1.生成Excel表格

使用huTool工具包的Excel表格生成功能

1.依赖设置

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.2</version>
</dependency>

Hutool-all中包含了Hutool的所有工具类,由于需要生成Excel文件需要依赖poi

2.代码:

    @Override
    public void publish(xxxxxxPublishVo publishVo) {
        /**
         * 生成Excel表格
         */
        //在内存操作,写到输出流中
        ExcelWriter writer = ExcelUtil.getWriter(true);
        //自定义标题别名
        writer.addHeaderAlias("projectCode", "xx编号");
        writer.addHeaderAlias("projectName", "xx名称");
        writer.addHeaderAlias("targetType", "xx类型");
        writer.addHeaderAlias("targetName", "xx名称");
        writer.addHeaderAlias("targetForMp", "xxxx目标");
        writer.addHeaderAlias("symbols", "xx限制符");
        //获取数据
        QpmxxxxTargetMg query = new QpmxxxxTargetMg();
        query.setProjectCode(publishVo.getProjectCode());
        List<xxxxxxListDTO> data =  selectxxxxxxByCondition(query);
 
        //整理数据,以便于生成Excel表格
        List<Object> dataNew = new ArrayList<>();
        Set<String> stageCollectSet = new HashSet<>();
        for (xxxxxxListDTO target: data){
            List<xxxxxxTargetMgStage> stageList = target.getxxxxxxStageList();
            Map<String, Object> addProperties = new HashMap<>();
            for (xxxxxxMgStage stage: stageList){
                if (!stageCollectSet.contains(stage)){
                    stageCollectSet.add(stage.getStage());
                    //给Excel增加列
                    writer.addHeaderAlias(stage.getStage(), stage.getStage() + "目标");
                }
                //为对象动态增加属性
                addProperties.put(stage.getStage(), stage.getStageTarget());
            }
 
            //生成新的包含了新增字段的对象
            Object targetNew = ReflectUtil.getTarget(target, addProperties);
            dataNew.add(targetNew);
        }
 
        //只保留别名的数据
        writer.setOnlyAlias(true);
        writer.write(dataNew,true);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 
        // excel写入到输出流
        writer.flush(outputStream,true);

上述代码中,调用了工具类ReflectUtil给对象动态增加属性。由于数据中有子类,需要获取到子类中的某个字段并生成Excel表格,所以Excel表格构造就需要对数据对象进行改造,简单来说就是需要给对象动态增加新的属性(成员对象的属性),如下所示示例:

把studentList里面的关键属性数据,新增给Test类

示例不是很合适,凑合着用吧

class Test {

    private String class;

    .............................................

    private List<Student> studentList;

}

工具类ReflectUtil,用于给对象动态增加新的属性:

import com.google.common.collect.Maps;
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 为实体类动态增加属性,用于生成Excel表格时的特殊情况,例如表格中的列需要动态增加
 */
public class ReflectUtil {
    static Logger logger = LoggerFactory.getLogger(ReflectUtil.class);
 
    public static Object getTarget(Object dest, Map<String, Object> addProperties) {
        // get property map
        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
        PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
        Map<String, Class> propertyMap = Maps.newHashMap();
        for (PropertyDescriptor d : descriptors) {
            if (!"class".equalsIgnoreCase(d.getName())) {
                propertyMap.put(d.getName(), d.getPropertyType());
            }
        }
        // add extra properties
        for (Map.Entry<String, Object> entry : addProperties.entrySet()) {
            propertyMap.put(entry.getKey(), entry.getValue().getClass());
        }
//        addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
        // new dynamic bean
        DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
        // add old value
        for (Map.Entry<String, Class> entry : propertyMap.entrySet()) {
            try {
                // filter extra properties
                if (!addProperties.containsKey(entry.getKey())) {
                    dynamicBean.setValue(entry.getKey(), propertyUtilsBean.getNestedProperty(dest, entry.getKey()));
                }
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        ;
        // add extra value
        for (Map.Entry<String, Object> entry : addProperties.entrySet()) {
            try {
                dynamicBean.setValue(entry.getKey(), entry.getValue());
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        ;
        Object target = dynamicBean.beanMap;
        return target;
    }
 
    public static class DynamicBean {
        /**
         * 目标对象
         */
        private Object target;
 
        /**
         * 属性集合
         */
        private BeanMap beanMap;
 
        public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
            this.target = generateBean(superclass, propertyMap);
            this.beanMap = BeanMap.create(this.target);
        }
 
 
        /**
         * bean 添加属性和值
         *
         * @param property
         * @param value
         */
        public void setValue(String property, Object value) {
            beanMap.put(property, value);
        }
 
        /**
         * 获取属性值
         *
         * @param property
         * @return
         */
        public Object getValue(String property) {
            return beanMap.get(property);
        }
 
        /**
         * 获取对象
         *
         * @return
         */
        public Object getTarget() {
            return this.target;
        }
 
 
        /**
         * 根据属性生成对象
         *
         * @param superclass
         * @param propertyMap
         * @return
         */
        private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
            BeanGenerator generator = new BeanGenerator();
            if (null != superclass) {
                generator.setSuperclass(superclass);
            }
            BeanGenerator.addProperties(generator, propertyMap);
            return generator.create();
        }
    }
}

至此,我们就生成了Excel表格,并且把数据写入到了输出流中。

下面我们需要从输出流中拿到Excel表格数据,并做为邮件的附件发送出去。

2.邮件发送

1.邮件发送功能实现-带附件

Spring Email 抽象的核心是 JavaMailSender接口,通过实现JavaMailSender接口把 Email 发送给邮件服务器,由邮件服务器实现邮件发送的功能。

Spring 自带了一个 JavaMailSender的实现 JavaMailSenderImpl。SpringBoot 应用在发送 Email 之前,我们需要在配置文件中对JavaMailSender进行属性配置,这样就可以利用Springboot的自动装配机制,将 JavaMailSenderImpl 装配为 Spring容器的一个 bean。

spring.mail.host: xxxxxxx.com
# 设置端口
spring.mail.port: 25
# 设置用户名
spring.mail.username: xxxxxxxxxx
# 设置密码,该处的密码是QQ邮箱开启SMTP的授权码而非QQ密码
spring.mail.password: xxxxxxxxx
# 设置是否需要认证,如果为true,那么用户名和密码就必须的,
# 如果设置false,可以不设置用户名和密码,当然也得看你的对接的平台是否支持无密码进行访问的。
spring.mail.properties.mail.smtp.auth: false
# STARTTLS[1]  是对纯文本通信协议的扩展。它提供一种方式将纯文本连接升级为加密连接(TLS或SSL),而不是另外使用一个端口作加密通信。
spring.mail.properties.mail.smtp.starttls.enable: true
spring.mail.properties.mail.smtp.starttls.required: fasle
spring.mail.properties.mail.imap.starttls.socketFactory.fallback: false
spring.mail.properties.mail.smtp.starttls.socketFactory.class: com.ey.model.MailCommand

继上面完整的Excel生成代码,现在继续写邮件发送代码:

    @Autowired
    private JavaMailSender springMailSender;    
 
    @Override
    public void publish(xxxxxxPublishVo publishVo) {
	    //........这里不再复制上面的代码,只从Excel表格写入输出流开始.........
        // excel写入输出流
        writer.flush(outputStream,true);
 
        //邮件附件名称
        String fileName = String.format("制定xx目标-%s.xlsx",UUID.randomUUID());
 
          //这个地方无需再配置,springboot自动装配,配置信息在nacos配置中心
//        springMailSender.setDefaultEncoding("UTF-8");
//        springMailSender.setHost("mx.goertek.com");
//        springMailSender.setPort(25);
//        springMailSender.setProtocol(JavaMailSenderImpl.DEFAULT_PROTOCOL);
//        springMailSender.setUsername("tims.sys@goertek.com");
//        springMailSender.setPassword("Khkd0804");
//        Properties p = new Properties();
//        p.setProperty("mail.smtp.timeout", "25000");
//        p.setProperty("mail.smtp.auth", "true");
//        p.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
//        springMailSender.setJavaMailProperties(p);
 
        MimeMessage mimeMessage = springMailSender.createMimeMessage();
        System.getProperties().setProperty("mail.mime.splitlongparameters", "false");
        MimeMessageHelper messageHelper = null;
        try {
            messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
        try {
            LoginUser userInfo = UserUtil.getCurrentUser();
//            String currentUserEmail = userInfo.getEmaila();
//            messageHelper.setFrom(currentUserEmail);
            messageHelper.setFrom(mailUserName);
 
            //设置收件人
            String[] emailArr = publishVo.getEmails().replaceAll("\\s+", "").split(",");
            messageHelper.setTo(emailArr);
 
            //设置抄送人
            if (!StringUtils.isBlank(publishVo.getCcEmails())){
                String[] ccEmailArr = publishVo.getCcEmails().replaceAll("\\s+", "").split(",");
                messageHelper.setCc(ccEmailArr);
            }
 
            messageHelper.setSubject("项目-" + publishVo.getProjectName().concat(": 制定品质目标完毕"));
            messageHelper.setText("项目-" + publishVo.getProjectName().concat(": 制定品质目标完毕"));
            try {
                //messageHelper.addInline("doge.gif", new File("xx/xx/doge.gif"));
                messageHelper.addAttachment(MimeUtility.encodeWord(fileName,"utf-8","B"), new ByteArrayResource(outputStream.toByteArray()));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            springMailSender.send(mimeMessage);
 
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }

2.踩过的坑

上述邮件发送功能实现过程中踩过的坑:

附件名中文乱码问题

附件的名字是中文,发送成功后,在邮件中的附件名字中文乱码,怎样解决这个问题?

1. 设置系统值:

System.setProperty("mail.mime.splitlongparameters", "false");

2. 这里,在创建对象的时候定义编码格式(utf-8):

MimeMessageHelper messageHelper = new MimeMessageHelper(mes, true, "utf-8");

3. 其次,在添加附件的时候,附件名是需要定义编码:

messageHelper.addAttachment(MimeUtility.encodeWord(附件名,"utf-8","B"), 附件输入流));

3.参考文章:

使用hutool工具进行导入导出excel表格_java_脚本之家 (jb51.net)

SpringBoot实现excel生成并且通过邮件发送的步骤详解_java_脚本之家 (jb51.net)

以上就是SpringBoot使用JavaMailSender实现发送邮件+Excel附件的详细内容,更多关于SpringBoot JavaMailSender发送邮件的资料请关注脚本之家其它相关文章!

相关文章

  • Java生成pdf文件或jpg图片的案例讲解

    Java生成pdf文件或jpg图片的案例讲解

    这篇文章主要介绍了Java生成pdf文件或jpg图片的案例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • 一篇文章带你了解JAVA面对对象之继承与修饰符

    一篇文章带你了解JAVA面对对象之继承与修饰符

    这篇文章主要介绍了Java面向对象编程之类的继承,结合实例形式较为详细的分析了Java面向对象编程类的概念、功能、使用方法及相关注意事项,需要的朋友可以参考下
    2021-08-08
  • Java中使用正则表达式的一个简单例子及常用正则分享

    Java中使用正则表达式的一个简单例子及常用正则分享

    这篇文章主要介绍了Java中使用正则表达式的一个简单例子及常用正则分享,本文用一个验证Email的例子讲解JAVA中如何使用正则,并罗列了一些常用的正则表达式,需要的朋友可以参考下
    2015-06-06
  • SpringBoot文件访问映射如何实现

    SpringBoot文件访问映射如何实现

    这篇文章主要介绍了SpringBoot文件访问映射如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java基于自定义类加载器实现热部署过程解析

    Java基于自定义类加载器实现热部署过程解析

    这篇文章主要介绍了Java基于自定义类加载器实现热部署过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • 解析Java的Spring框架的基本结构

    解析Java的Spring框架的基本结构

    这篇文章主要介绍了Java的Spring框架的基本结构,作者从Spring的设计角度触发解析其基础的架构内容,需要的朋友可以参考下
    2016-03-03
  • spring boot整合redis实现RedisTemplate三分钟快速入门

    spring boot整合redis实现RedisTemplate三分钟快速入门

    这篇文章主要介绍了spring boot整合redis实现RedisTemplate三分钟快速入门,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Java多线程工具CompletableFuture详解

    Java多线程工具CompletableFuture详解

    这篇文章主要介绍了Java多线程工具CompletableFuture详解,CompletableFuture 是 java 1.8 追加的新特性,通俗的话来说,是一个函数式的,用于控制多任务同步、异步组合操作的工具,需要的朋友可以参考下
    2024-01-01
  • Spring WebClient实战示例

    Spring WebClient实战示例

    本文主要介绍了Spring WebClient实战示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • SpringBoot整合WebService的实现示例

    SpringBoot整合WebService的实现示例

    本文主要介绍了SpringBoot整合WebService,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论