Thymeleaf发送邮件

发布时间:2023年12月22日
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

简单样式邮件

对于消息提醒,目前的软件开发越来越倾向于发送短信或使用IM,但邮件依旧有着不可代替的作用,比如正式的面试邀请及offer发放等。

使用邮件发送验证码时,可能并不需要过多样式设计,可以直接在Java代码中拼接HTML:

@Test
public void testSimpleMail() {
    String receiver = "523839773@qq.com";
    String code = "123456";

    MimeMessage message = javaMailSender.createMimeMessage();
    try {
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(sender, "知乎");
        helper.setTo(receiver);
        helper.setSubject("测试验证码发送");
        // 接收调用方传入的验证码
        String VALIDATE_CODE_TEMPLATE = "<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "  <head>\n" +
                "    <meta charset=\"UTF-8\" />\n" +
                "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n" +
                "    <title>验证您的知乎邮箱账户</title>\n" +
                "  </head>\n" +
                "  <body>\n" +
                "    <h2 style=\"text-align: center;\">验证您的知乎邮箱账户</h2>\n" +
                "    <p style=\"text-indent: 2em;\">hi~</p>\n" +
                "    <p style=\"text-indent: 2em;\">\n" +
                "      您已选择 $EMAIL$ 作为你的邮箱账户,为验证此电子邮箱属于你,请在你的邮箱验证界面输入下方6位验证码\n" +
                "    </p>\n" +
                "    <h1 style=\"color: #1890ff; text-align: center;\">\n" +
                "      $CODE$\n" +
                "    </h1>\n" +
                "    <p style=\"text-indent: 2em;\">此验证码将于2小时后失效,请您尽快完成验证。</p>\n" +
                "  </body>\n" +
                "</html>";
        String text = VALIDATE_CODE_TEMPLATE.replace("$EMAIL$", receiver).replace("$CODE$", code);
        int i = 1 / 0;
        helper.setText(text, true);
        // 发送填充好的整个html页面
        javaMailSender.send(message);
        log.info("验证码发送成功");
    } catch (Exception e) {
        log.info("给邮箱{}发送验证码时发生错误", receiver);
        e.printStackTrace();
    }
}

虽然我说是Java代码里直接拼接HTML,但你可千万别真的直接在代码里拼...先让前端同事写好静态页面,然后拷贝整个HTML复制粘贴就好了:

最终效果:

但相信大家也看到过类似下面这种邮件通知:

是的,你会感觉邮件里好像内嵌了一个页面,而且这个页面上的链接是可以点击跳转的。

要实现这种效果,本质上还是发送一段text文本,只不过text文本中的HTML样式比之前验证码的HTML更加丰富了。而且个别情况下,我们还需要根据用户的性别分别显示♂、♀,也就是需要按条件显示。

此时可以结合SpringBoot Mail + Thymeleaf,以Thymeleaf为页面模板,把需要替换的数据交给模板引擎渲染成静态页面,最后作为邮件的text发送即可。

有些人可能觉得前后端分离的时代,根本不需要Thymeleaf这种模板技术。其实模板技术未必要用来写前端页面,打打辅助可能会有意想不到的收获。

复杂样式邮件Demo

如果发送失败,请百度,需要设置一下QQ邮箱。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
spring.mail.host=smtp.xxx.cn
# 邮箱用户名
spring.mail.username=xxx@yyy.cn
# 邮箱密码(注意:qq邮箱应该使用独立密码,去qq邮箱设置里面获取)
spring.mail.password=xxxx
# 编码格式
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.port=465
spring.mail.properties.mail.smtp.socketFactory.port = 465
spring.mail.properties.mail.smtp.socketFactory.class = javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.socketFactory.fallback = false
# java mail发邮件是附件名过长默认会被截断,附件名显示【tcmime.29121.xxx.xxx.bin】,主动设为false可正常显示附件名
spring.mail.properties.mail.mime.splitlongparameters=false
/**
 * @author MX
 */
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringMailTest {

    @Value("${spring.mail.username}")
    private String sender;
    @Autowired
    private JavaMailSender javaMailSender;
    @Autowired
    private TemplateEngine templateEngine;

    @Test
    public void sendResumeNotify() {
        MimeMessage message = javaMailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(sender, "知乎");
            helper.setTo("523839773@qq.com");
            helper.setSubject("简历投递提醒");

            // 定义模板数据
            Context context = new Context();
            HashMap<String, Object> paramMap = new HashMap<>(16);
            paramMap.put("candidate", "bravo1988");
            paramMap.put("sex", 1);
            paramMap.put("publisher", "马云");
            paramMap.put("jobName", "Java高级开发工程师(saas方向)");
            paramMap.put("icon", "https://pic4.zhimg.com/v2-1907eb21be63d35b077e6ed3cbcbfe13_xll.jpg");
            paramMap.put("school", "清华大学");
            paramMap.put("major", "计算机科学与技术");
            paramMap.put("education", "本科");
            paramMap.put("status", "已离职");
            paramMap.put("expectJob", "软件工程师");
            paramMap.put("expectCity", "杭州");
            paramMap.put("salaryBegin", 10);
            paramMap.put("salaryEnd", 12);
            paramMap.put("appendixUrl", "https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1601035929262.pdf");
            paramMap.put("appendixName", "bravo1988_Java.pdf");
            context.setVariables(paramMap);
            // 获取thymeleaf模板,填充数据
            String emailContent = templateEngine.process("resume/notifyHR", context);
            helper.setText(emailContent, true);
            // 发送填充好的整个html页面
            javaMailSender.send(message);
            log.info("简历投递提醒发送成功。");
        } catch (Exception e) {
            log.error("简历投递提醒发送失败!", e);
        }
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div class="resume-wrap">
    <div class="resume-header">
<!--      <img class="logo-title" src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1601028532357_430*289.jpg">-->
      <span style="margin-right: 10px;">知乎</span>
      <div class="header-title">你好</div>
    </div>
    <div class="resume-body">
      <p>Hi, <span th:text="${publisher}">马云</span></p>
      <div class="body_content">你在知乎上发布的<span class="resume_position" th:text="${jobName}">销售经理-杭州</span>职位,有候选人感兴趣,并通过邮箱给你发了一封简历,请点击邮箱附件查看其附件简历。</div>
      <div class="position-card">
        <div class="avatar">
          <img th:if="${sex eq 1}" src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1600923429345_60*60.png" alt="" class="avatar-sex-icon">
          <img th:if="${sex eq 0}" src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1600923451797_60*60.png" alt="" class="avatar-sex-icon">
          <img th:src="${icon}" alt="">
        </div>
        <div class="card-info">
          <div class="info-name"><span th:text="${candidate}">陈先生</span></div>
          <div class="info-pos">
            <span th:text="${school}">上海交通大学</span>
            <span th:text="${major}">软件工程</span>
            <span th:text="${education}">学士</span>
            <span th:text="${salaryBegin}">6</span>K-<span th:text="${salaryEnd}">8</span>K / 月</span>
          </div>
          <div class="info-duty info-bottom">
            <img src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1600923405293_23*23.png" alt="职位图标">
            <span th:text="${expectJob}">Java开发工程师</span>
          </div>
          <div class="info-address info-bottom">
            <img src="https://wise-job.oss-cn-zhangjiakou.aliyuncs.com/wiseJob/1600923377292_23*23.png" alt="地理位置">
            <span th:text="${expectCity}">杭州</span>
          </div>
        </div>
      </div>
      <div style="text-align: center;">
        <a class="resume-bottom" th:href="${appendixUrl}" target="_blank">查看<span th:text="${appendixName}"></span>附件简历</a>
      </div>
    </div>
  </div>
</body>
</html>
<style>
  .resume-wrap {
    width: 621px;
    margin: 0 auto;
    background: #FFFFFF;
    border-radius: 4px;
    box-shadow: 0px 0px 18px 0px rgba(21, 21, 22, 0.17);
  }
  .logo-title {
    height: 25px;
    margin-right: 14px;
  }
  .header-title {
    padding-left: 14px;
    border-left: 1px solid #fff;
  }
  .resume-header {
    display: flex;
    align-items: center;
    padding: 0 22px;
    font-size: 20px;
    color: #fff;
    height: 75px;
    border-radius: 4px 4px 0px 0px;
    background: linear-gradient(251deg, #5FAEF9 0%, #3985FD 100%);
  }
  .resume-body {
    padding: 50px;
    color: #131415;
  }
  .resume_position {
    color:#5FAEF9;
  }
  .body_content {
    line-height: 25px;
  }
  .position-card {
    display: flex;
    margin-top: 36px;
    border-radius: 4px;
    border: 1px solid #DBE0E6;
    padding: 30px;
  }
  .avatar {
    position: relative;
    width: 60px;
    height: 60px;
    border-radius: 50%;
  }
  .avatar img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    border: 1px solid #DBE0E6;
  }
  .avatar .avatar-sex-icon {
    position: absolute;
    width: 16px;
    height: 16px;
    bottom: 0;
    right: 0;
  }
  .card-info {
    margin-left: 15px;
  }
  .info-name {
    font-size: 18px;
    font-weight: 600;
    color: #3C3E43;
    line-height: 25px;
  }
  .info-pos {
    margin-top: 10px;
    margin-bottom: 16px;
    color: #3C3E43;
    font-size: 14px;
  }
  .info-pos span {
    padding: 0 10px;
    border-right: 1px solid #DBE0E6;
  }
  .info-pos span:first-child {
    padding-left: 0;
  }
  .info-pos span:nth-child(n+4) {
    padding-right: 0;
    border-right: none;
  }
  .info-pos span:nth-child(n+5) {
    padding: 0;
  }
  .info-bottom {
    font-size: 14px;
    font-weight: 400;
    color: #7F8289;
    line-height: 20px;
    margin-bottom: 10px;
  }
  .info-bottom img{
    width: 16px;
    height: 16px;
    vertical-align: middle;
    margin-left: 2px;
  }
  .resume-bottom {
    margin: 46px auto 0;
    color: #fff;
    padding:0 20px;
    /*width: 230px;*/
    height: 44px;
    line-height: 44px;
    /*background: #F9791B;*/
    border-radius: 4px;
    text-align: center;
    cursor: pointer;
    background:  #5FAEF9;
  }
  a {
    display: inline-block;
    margin: 10px;
    color: #F9791B;
  }
  .resume-awrap {
    height: 30px;
  }
</style>

稍微提一下模板路径的问题:Thymeleaf默认目录是templates,默认模板后缀是.html,所以可以都省略。

效果展示:

这样式,感觉还蛮好看的。(^..^)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬
文章来源:https://blog.csdn.net/smart_an/article/details/135144307
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。