发送邮件

一. 首先你必须要有一个邮箱 126 163 等基本都可以,但是不排除有些邮箱不支持

步骤一: 开启邮箱的SMTP服务

开启126邮箱smtp服务.png

步骤二:开启授权码

image.png

二. 添加依赖

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
      <!--本示例使用freemarker作为模板邮件的模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

三. 在application.properties添加邮箱配置

#协议
spring.mail.protocol=smtp
#发送者的邮箱账号,不要加@xx.com
spring.mail.username=ouranxxxx
#邮箱服务器,根据自己的邮箱设置
spring.mail.host=smtp.126.com
#发邮件send_name,必须跟邮箱保持一致,不然会报错
mail.send_username=ourxxxx6@126.com
#邮箱授权码,为了安全,使用授权码而不是密码
spring.mail.password=xxx123456

三. 发送邮件工具类

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.validation.Valid;
import java.io.File;
import java.util.*;

@Service
public class JMailService {
    private static final String DEFAULT_ENCODING = "utf-8";

    @Value("${mail.send_username}")
    private String SEND_USERNAME;

    @Autowired
    JavaMailSender javaMailSender;

    private static final Logger logger = LoggerFactory.getLogger(JMailService.class);

    private static Template template;
    private static Configuration configuration;

    static {
        try {
            //配置邮件模板路径
            String templateDir = JMailService.class.getClassLoader().getResource("templates/email").getPath();
            //创建配置对象
            configuration = new Configuration(Configuration.getVersion());
            //设置模板文件所在路径
            configuration.setDirectoryForTemplateLoading(new File(templateDir));
            //设置编码格式
            configuration.setDefaultEncoding("utf-8");

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("freemarker template初始化失败");
        }

    }

    /**
     * 发送一个简单的文本邮件,可以附带附件:文本邮件发送的基本方法
     *
     * @param subject:邮件主题,即邮件的邮件名称
     * @param content:邮件内容
     * @param toWho:需要发送的人
     * @param ccPeoples:需要抄送的人
     * @param bccPeoples:需要密送的人
     * @param attachments:需要附带的附件,附件请保证一定要存在,否则将会被忽略掉
     */
    public void sendSimpleTextMail(String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, String[] attachments) {

        //检验参数:邮件主题、收件人、邮件内容必须不为空才能够保证基本的逻辑执行
        if (subject == null || toWho == null || toWho.length == 0 || content == null) {
            logger.error("邮件-> {} 无法继续执行,因为缺少基本的参数:邮件主题、收件人、邮件内容", subject);
            throw new RuntimeException("模板邮件无法继续发送,因为缺少必要的参数!");
        }

        logger.info("开始发送简单文本邮件:主题->{},收件人->{},抄送人->{},密送人->{},附件->{}", subject, toWho, ccPeoples, bccPeoples, attachments);

        //附件处理,需要处理附件时,需要使用二进制信息,使用 MimeMessage 类来进行处理
        if (attachments != null && attachments.length > 0) {

            try {
                //附件处理需要进行二进制传输
                MimeMessage mimeMessage = javaMailSender.createMimeMessage();

                MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);

                //设置邮件的基本信息:这些函数都会在后面列出来
                boolean continueProcess = handleBasicInfo(helper, subject, content, toWho, ccPeoples, bccPeoples, false);

                //如果处理基本信息出现错误
                if (!continueProcess) {
                    logger.error("邮件基本信息出错: 主题->{}", subject);
                    return;
                }
                //处理附件
                handleAttachment(helper, subject, attachments);
                //发送该邮件
                javaMailSender.send(mimeMessage);
                logger.info("发送邮件成功: 主题->{}", subject);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("发送邮件失败: 主题->{}", subject);
            }
        } else {

            //创建一个简单邮件信息对象
            SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
            //设置邮件的基本信息
            handleBasicInfo(simpleMailMessage, subject, content, toWho, ccPeoples, bccPeoples);
            //发送邮件
            javaMailSender.send(simpleMailMessage);
            logger.info("发送邮件成功: 主题->{}", subject, toWho, ccPeoples, bccPeoples, attachments);
        }
    }

    /**
     * 处理二进制邮件的基本信息,比如需要带附件的文本邮件、HTML文件、图片邮件、模板邮件等等
     *
     * @param mimeMessageHelper:二进制文件的包装类
     * @param subject:邮件主题
     * @param content:邮件内容
     * @param toWho:收件人
     * @param ccPeoples:抄送人
     * @param bccPeoples:暗送人
     * @param isHtml:是否是HTML文件,用于区分带附件的简单文本邮件和真正的HTML文件
     * @return :返回这个过程中是否出现异常,当出现异常时会取消邮件的发送
     */
    private boolean handleBasicInfo(MimeMessageHelper mimeMessageHelper, String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, boolean isHtml) {

        try {
            //设置必要的邮件元素

            //设置发件人
            mimeMessageHelper.setFrom(SEND_USERNAME);
            //设置邮件的主题
            mimeMessageHelper.setSubject(subject);
            //设置邮件的内容,区别是否是HTML邮件
            mimeMessageHelper.setText(content, isHtml);
            //设置邮件的收件人
            mimeMessageHelper.setTo(toWho);

            //设置非必要的邮件元素,在使用helper进行封装时,这些数据都不能够为空
            if (ccPeoples != null && ccPeoples.length > 0)
                //设置邮件的抄送人:MimeMessageHelper # Assert.notNull(cc, "Cc address array must not be null");
                mimeMessageHelper.setCc(ccPeoples);

            if (bccPeoples != null && ccPeoples.length > 0)
                //设置邮件的密送人:MimeMessageHelper # Assert.notNull(bcc, "Bcc address array must not be null");
                mimeMessageHelper.setBcc(bccPeoples);

            return true;
        } catch (MessagingException e) {
            e.printStackTrace();
            logger.error("邮件基本信息出错->{}", subject);
        }
        return false;
    }

    /**
     * 用于填充简单文本邮件的基本信息
     *
     * @param simpleMailMessage:文本邮件信息对象
     * @param subject:邮件主题
     * @param content:邮件内容
     * @param toWho:收件人
     * @param ccPeoples:抄送人
     * @param bccPeoples:暗送人
     */
    private void handleBasicInfo(SimpleMailMessage simpleMailMessage, String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples) {

        //设置发件人
        simpleMailMessage.setFrom(SEND_USERNAME);
        //设置邮件的主题
        simpleMailMessage.setSubject(subject);
        //设置邮件的内容
        simpleMailMessage.setText(content);
        //设置邮件的收件人
        simpleMailMessage.setTo(toWho);
        //设置邮件的抄送人
        simpleMailMessage.setCc(ccPeoples);
        //设置邮件的密送人
        simpleMailMessage.setBcc(bccPeoples);
    }

    /**
     * 用于处理附件信息,附件需要 MimeMessage 对象
     *
     * @param mimeMessageHelper:处理附件的信息对象
     * @param subject:邮件的主题,用于日志记录
     * @param attachmentFilePaths:附件文件的路径,该路径要求可以定位到本机的一个资源
     */
    private void handleAttachment(MimeMessageHelper mimeMessageHelper, String subject, String[] attachmentFilePaths) {

        //判断是否需要处理邮件的附件
        if (attachmentFilePaths != null && attachmentFilePaths.length > 0) {
            FileSystemResource resource;
            String fileName;
            //循环处理邮件的附件
            for (String attachmentFilePath : attachmentFilePaths) {
                //获取该路径所对应的文件资源对象
                resource = new FileSystemResource(new File(attachmentFilePath));
                //判断该资源是否存在,当不存在时仅仅会打印一条警告日志,不会中断处理程序。
                // 也就是说在附件出现异常的情况下,邮件是可以正常发送的,所以请确定你发送的邮件附件在本机存在
                if (!resource.exists()) {
                    logger.warn("邮件->{} 的附件->{} 不存在!", subject, attachmentFilePath);
                    //开启下一个资源的处理
                    continue;
                }

                //获取资源的名称
                fileName = resource.getFilename();
                try {
                    //添加附件
                    mimeMessageHelper.addAttachment(fileName, resource);
                } catch (MessagingException e) {
                    e.printStackTrace();
                    logger.error("邮件->{} 添加附件->{} 出现异常->{}", subject, attachmentFilePath, e.getMessage());
                }
            }
        }
    }

    /**
     * 可以用来发送带有图片的HTML模板邮件
     *
     * @param subject:邮件主题
     * @param toWho:收件人
     * @param ccPeoples:抄送人
     * @param bccPeoples:暗送人
     * @param attachments:附件
     * @param templateName:模板名称
     * @param context:模板解析需要的数据
     * @param imageResourceSet:图片资源的资源对象
     */
    public void sendHtmlTemplateMail(String subject, String[] toWho, String[] ccPeoples, String[] bccPeoples, String[] attachments, String templateName, Map context, Set<ImageResource> imageResourceSet) {
        //加载指定的模板
        try {
            template = configuration.getTemplate(templateName);
            //检验参数:邮件主题、收件人、模板名称必须不为空才能够保证基本的逻辑执行
            if (subject == null || toWho == null || toWho.length == 0 || templateName == null) {
                logger.error("邮件-> {} 无法继续执行,因为缺少基本的参数:邮件主题、收件人、模板名称", subject);
                throw new RuntimeException("模板邮件无法继续发送,因为缺少必要的参数!");
            }

            //日志这个邮件的基本信息
            logger.info("发送HTML模板邮件:主题->{},收件人->{},抄送人->{},密送人->{},附件->{},模板名称->{},模板解析参数->{},图片资源->{})", subject, toWho, ccPeoples, bccPeoples, attachments, templateName, context, imageResourceSet);

            //context不能够为空,需要进行检查
            if (context == null) {
                context = new HashMap();
                logger.info("邮件->{} 的context为空!", subject);
            }

            //模板引擎处理模板获取到HTML字符串
            String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, context);
            ;
            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            //默认编码为UTF-8
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
            //处理内联的图片资源的占位转换
            content = handleInLineImageResourceContent(helper, subject, content, imageResourceSet);
            logger.info("解析邮件结果->{}", content);
            //处理基本信息
            boolean continueProcess = handleBasicInfo(helper, subject, content, toWho, ccPeoples, bccPeoples, true);
            if (!continueProcess) {
                logger.error("邮件基本信息出错:主题->{}", subject);
                return;
            }

            //内联资源的资源附加,这个必须要放置在设置基本信息的操作后面,或者是全部内容解析完毕后才可以,不能边解析,边占位
            handleInLineImageResource(helper, subject, imageResourceSet);
            //处理附件
            handleAttachment(helper, subject, attachments);
            //发送该邮件
            javaMailSender.send(mimeMessage);
            logger.info("发送邮件成功:主题->{}", subject);

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("发送邮件失败:" + e.getMessage() + "邮件主题->{}", subject);
        }
    }

    /**
     * 处理内嵌图片的模板HTML邮件,返回一个已经修改过后的HTML字符串
     *
     * @param mimeMessageHelper:邮件信息包装类
     * @param subject:邮件主题
     * @param originContent:模板引擎所解析出来的原始HTML邮件
     * @param imageResourceSet:图片资源集合,用于字符集站位填充
     * @return :返回处理后的邮件内容
     */
    private String handleInLineImageResourceContent(MimeMessageHelper mimeMessageHelper, String subject, String originContent, Set<ImageResource> imageResourceSet) {

        //处理内嵌的HTML图片文件
        if (imageResourceSet != null && imageResourceSet.size() > 0) {
            //资源的占位符ID
            String rscId;
            //资源的路径
            String resourcePath = null;
            //图片的位置信息
            String placeHolder;
            //图片资源文件
            FileSystemResource resource;
            for (ImageResource imageResource : imageResourceSet) {
                //获取图片资源的基本信息
                rscId = imageResource.getId();
                placeHolder = imageResource.getPlaceholder();
                resourcePath = imageResource.getImageFilePath();
                resource = new FileSystemResource(new File(resourcePath));
                //判断图片资源是否存在
                if (!resource.exists()) {
                    logger.warn("邮件->{} 内联图片->{} 找不到", subject, resourcePath);
                    continue;
                }
                //替换图片资源在HTML中的位置
                originContent = originContent.replace("\"" + ImageResource.PLACEHOLDERPREFIX + placeHolder + "\"", "\'cid:" + rscId + "\'");
            }
        }
        return originContent;
    }

    /**
     * 填充文本数据,因为数据填充必须在设置基本数据后面进行,所以讲内容和数据的填充进行分离
     *
     * @param mimeMessageHelper
     * @param subject:邮件主题,用于日志记录
     * @param imageResourceSet:资源
     */
    private void handleInLineImageResource(MimeMessageHelper mimeMessageHelper, String subject, Set<ImageResource> imageResourceSet) {

        if (imageResourceSet != null && imageResourceSet.size() > 0) {
            FileSystemResource resource;
            for (ImageResource imageResource : imageResourceSet) {
                resource = new FileSystemResource(new File(imageResource.getImageFilePath()));
                if (!resource.exists()) {
                    logger.warn("邮件->{} 的内联图片文件->{} 不存在!", subject, imageResource);
                    continue;
                }

                try {
                    //添加内联资源
                    mimeMessageHelper.addInline(imageResource.getId(), resource);
                } catch (MessagingException e) {
                    e.printStackTrace();
                    logger.error("邮件->{} 的内联图片文件->{} 添加错误!", subject, imageResource);
                }
            }
        }
    }

    /**
     * 传入的参数不能够为null
     *
     * @param args
     * @return
     */
    private boolean assertNotNull(Object... args) {
        return Arrays.stream(args).noneMatch(Objects::isNull);
    }


    /**
     * 用于支撑HTML内嵌图片的支持类,拥有可以传输内联图片的全部基本信息
     */
    public final static class ImageResource {

        //占位符的前缀符号,用于替换字符串定位,比如:image1 在模板文件里面需要写成 #image1
        public static final String PLACEHOLDERPREFIX = "#";

        //用于文件区分,实现图片文件内联邮件发送
        private final String id;

        //这个图片需要填充到那个地方去,这个地方是一个标识,为了和其他标签区别开来,使用前缀加上标识符来进行区分,比如 :#imageOrigin
        private final String placeholder;

        //图片的文件路径,该文件路径必须是本机文件系统的绝对路径,即可以直接 new File 的文件系统路径
        private final String imageFilePath;

        public ImageResource(String placeholder, String imageFilePath) {
            this.placeholder = placeholder;
            this.imageFilePath = imageFilePath;
            //自动生成id,用于区分图片文件
            this.id = String.valueOf(System.nanoTime());
        }

        public String getId() {
            return id;
        }

        public String getPlaceholder() {
            return placeholder;
        }

        public String getImageFilePath() {
            return imageFilePath;
        }

        @Override
        public String toString() {
            return "ImageResource{" + "id=" + id + ", placeholder='" + placeholder + '\'' + ", imageFilePath='" + imageFilePath + '\'' + '}';
        }
    }
}

测试类

package cn.wolfcode.shop.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.mail.MessagingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Controller
public class MailTestController {

    @Autowired
    JMailService jMailService;

    @Autowired
    private JavaMailSender mailSender;


    //请把如下的信息写成你自己的邮箱信息
    //密送邮箱
    private String mail2 = "10xxxx2926@qq.com";
    //抄送邮箱
    private String mail3 = "474xxxx78@qq.com";
    private String mail4 = "ourxxxx6@aliyun.com";
    //收件人邮箱
    private String mail5 = "ouraxxxx6@126.com";
    //邮件内容
    private String content = "简单的文本内容";
    //三个附件
    private String zipFile = "C:\\Users\\Administrator\\Desktop\\abc.zip";
    private String pngFile = "C:\\Users\\Administrator\\Desktop\\abc.png";
    private String docFile = "C:\\Users\\Administrator\\Desktop\\abc.docx";

    @Autowired
    JavaMailSender javaMailSender;


    //最简单测试
    @GetMapping("/mail")
    @ResponseBody
    public String contextLoads() {
        //建立邮件消息
        SimpleMailMessage mainMessage = new SimpleMailMessage();
        //发送者
        mainMessage.setFrom(mail5);
        //接收者
        mainMessage.setTo(mail5);
        //发送的标题
        mainMessage.setSubject("测试简单邮件发送");
        //发送的内容
        mainMessage.setText("System.out.println(hello world)");
        javaMailSender.send(mainMessage);
        return "success";
    }

    /**
     * 测试发送文本邮件的接口
     */
    @GetMapping("/textmail")
    @ResponseBody
    public String  sendSimpleTextMail() throws InterruptedException{
        //发送一封邮件,抄送给两个邮箱,带着三个附件
        jMailService.sendSimpleTextMail("测试带附件、有抄送、密送的多收件人简单文本文件",content,new String[]{mail5},
                null,null,new String[]{zipFile, docFile,pngFile});
        return "success";
    }

    /***
     * 测试模板HTML邮件
     */
    @GetMapping("/templatemail")
    @ResponseBody
    public String sendHtmlTemplateMail() throws MessagingException {

        //模板解析的上下文
        Map context = new HashMap<>();
        context.put("name","秦羽");
        context.put("age","20");
        context.put("sex","男");
        context.put("token","123456");


        //邮箱中显示图片需要经过处理:设置内联的图片资源,placeholder和html模板中占位符对应
        JMailService.ImageResource imageResource1 = new JMailService.ImageResource("phA",pngFile);
        Set<JMailService.ImageResource> imageResources = new HashSet<>();
        imageResources.add(imageResource1);
        jMailService.sendHtmlTemplateMail(
                "测试模板邮件",  //主题
                new String[]{mail5},            //收件人
                null,                  //抄送人
                null,                 //密送人
                new String[]{zipFile,docFile},  //附件
                "validate.ftl",    //模板名称
                context,                        //数据模型
                imageResources);                //需要处理的图片资源
        return "success";
    }

    /**
     * 测试使用邮箱完成注册用户校验
     * @return
     */
    @GetMapping("/validate")
    @ResponseBody
    public String testValidate(String token){
        System.out.println("=============测试使用邮件校验,token "+token);
        return "you mail validate accept ...";
    }

}

需要用到的资源

  1. 三个附件文件

2.validate.ftl 放在springboot项目的 templates/emial文件夹下

validate.ftl文件

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>邮件模板</title>
</head>
<body>
<h2>你好!这是一份来自未来的邮件!</h2>
<p>姓名 <span>${name}</span></p><br/>
<p>年龄 <span>${age}</span></p><br/>
<p>性别 <span>${sex}</span></p><br/>
<img src="#phA" width="100px" height="100px"/><br/>
<a href="localhost:80/validate?token=${token}" target="_blank" rel="noreferrer">请点击链接校验您的邮箱</a>
</body>
</html>

============================完=========================

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容