Springboot 使用JavaMailSender发送邮件 + Excel附件

目录

1.生成Excel表格

1.依赖设置

2.代码:

2.邮件发送

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

2.踩过的坑

1.附件名中文乱码问题

3.参考文章:


需求描述:项目审批完毕后,需要发送邮件通知相关人员,并且要附带数据库表生成的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.附件名中文乱码问题

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

  1. 设置系统值:

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

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

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

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

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

3.参考文章:

使用hutool工具进行导入导出excel表格_hutool excel-CSDN博客

springboot:实现excel生成并且通过邮件发送 - 哔哩哔哩

相关推荐
摇滚侠2 小时前
Spring Boot 3零基础教程,WEB 开发 静态资源默认配置 笔记27
spring boot·笔记·后端
wb043072013 小时前
性能优化实战:基于方法执行监控与AI调用链分析
java·人工智能·spring boot·语言模型·性能优化
Chen-Edward6 小时前
有了Spring为什么还有要Spring Boot?
java·spring boot·spring
magic334165636 小时前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
小学鸡!7 小时前
Spring Boot实现日志链路追踪
java·spring boot·后端
番茄Salad7 小时前
Spring Boot临时解决循环依赖注入问题
java·spring boot·spring cloud
摇滚侠11 小时前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠11 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
橘子海全栈攻城狮11 小时前
【源码+文档+调试讲解】基于SpringBoot + Vue的知识产权管理系统 041
java·vue.js·人工智能·spring boot·后端·安全·spring
Json_12 小时前
学习springBoot框架-开发一个酒店管理系统,熟悉springboot框架语法~
java·spring boot·后端