Отправить электронную почту за десять минут

Spring Boot
Отправить электронную почту за десять минут

Отправка электронных писем должна быть одним из необходимых расширений веб-сайта, таких как проверка регистрации, забвение паролей или отправка маркетинговых сообщений пользователям.

1. Почтовое соглашение

В процессе отправки и получения электронных писем вам необходимо соблюдать соответствующие протоколы, которые в основном включают:

  1. Протокол отправки писем:SMTP;
  2. Согласие на получение писем:POP3а такжеIMAP.

1.1 Что такоеSMTP?

SMTPполное имяSimple Mail Transfer Protocol(Простой протокол передачи почты), который представляет собой набор спецификаций для передачи почты с адреса источника на адрес назначения, с помощью которых он контролирует ретрансляцию почты.SMTPАутентификация требует учетную запись и пароль для входа на сервер и предназначена для защиты пользователей от спама.

1.2 Что такоеIMAP?

IMAPполное имяInternet Message Access Protocol(протокол доступа к электронной почте в Интернете),IMAPПозволяет получать информацию о почте, загружать почту и т. д. с почтового сервера.IMAPа такжеPOPАналогично, это протокол получения почты.

1.3 Что такоеPOP3?

POP3полное имяPost Office Protocol 3(Договор почтового отделения),POP3Поддержка удаленного управления почтой на стороне клиента на стороне сервера.POP3обычно используетсяне в сетиОбработка почты, которая позволяет клиенту загружать почту сервера, после чего почта на сервере будет удалена. В настоящее время многиеPOP3Почтовый сервер предоставляет только функцию скачивания почты, сам сервер не удаляет почту, это улучшенная версияPOP3протокол.

1.4 IMAPа такжеPOP3Чем отличается договор?

Самая большая разница между ними заключается в том, чтоIMAPДопускается двусторонняя связь, то есть операции на стороне клиента будут передаваться обратно на сервер, например, получение писем на стороне клиента, пометка как прочитанные и т. д., и сервер будет синхронизировать эти операции. И дляPOPХотя протокол также позволяет клиенту загружать почту сервера, операции на клиенте не будут синхронизированы с сервером, такие как получение или пометка прочитанной почты на клиенте, сервер не будет синхронизировать эти операции.

2. Начальная конфигурация

2.1 Откройте почтовый сервис

В этой статье используется толькоQQпочтовый ящик и163Возьмите электронную почту в качестве примера.

  1. QQПочтовый ящик Открыть документ почтовой службы
  2. 163Почтовый ящик Открыть документ почтовой службы

2.2 pom.xml

Обычно мы будем использоватьJavaMailСвязанныйapiнаписать соответствующий код для отправки почты, но сейчасSpring BootПредоставляет набор более простых в использовании пакетов.

  1. spring-boot-starter-mail:Spring Bootпочтовая служба;
  2. spring-boot-starter-thymeleaf:использоватьThymeleafСоздание шаблонов электронной почты.
<!-- test 包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--使用 Thymeleaf 制作邮件模板 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>1.8.4</scope>
</dependency>

2.3 application.yml

spring-boot-starter-mailнастраиваетсяMailPropertiesПредусмотрены классы конфигурации.

Конфигурация для разных почтовых ящиков немного отличается, следующиеQQпочтовый ящик и163Конфигурация почтового ящика.

server:
  port: 8081
#spring:
#  mail:
#    # QQ 邮箱 https://service.mail.qq.com/cgi-bin/help?subtype=1&&no=1001256&&id=28
#    host: smtp.qq.com
#    # 邮箱账号
#    username: van93@qq.com
#    # 邮箱授权码(不是密码)
#    password: password
#    default-encoding: UTF-8
#    properties:
#      mail:
#        smtp:
#          auth: true
#          starttls:
#            enable: true
#            required: true
spring:
  mail:
    # 163 邮箱 http://help.mail.163.com/faqDetail.do?code=d7a5dc8471cd0c0e8b4b8f4f8e49998b374173cfe9171305fa1ce630d7f67ac2cda80145a1742516
    host: smtp.163.com
    # 邮箱账号
    username: 17098705205@163.com
    # 邮箱授权码(不是密码)
    password: password
    default-encoding: UTF-8
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true

2.4 Класс почтовой информации

Чтобы сохранить тему электронной почты, содержимое электронной почты и другую информацию при отправке электронной почты

@Data
public class Mail {
    /**
     * 邮件id
     */
    private String id;
    /**
     * 邮件发送人
     */
    private String sender;
    /**
     * 邮件接收人 (多个邮箱则用逗号","隔开)
     */
    private String receiver;
    /**
     * 邮件主题
     */
    private String subject;
    /**
     * 邮件内容
     */
    private String text;
    /**
     * 附件/文件地址
     */
    private String filePath;
    /**
     * 附件/文件名称
     */
    private String fileName;
    /**
     * 是否有附件(默认没有)
     */
    private Boolean isTemplate = false;
    /**
     * 模版名称
     */
    private String emailTemplateName;
    /**
     * 模版内容
     */
    private Context emailTemplateContext;

}

В-третьих, реализация отправки почты

3.1 Проверьте введенную конфигурацию почты

Проверьте обязательные поля получателя электронной почты, темы электронной почты и содержимого электронной почты.

private void checkMail(Mail mail) {
    if (StringUtils.isEmpty(mail.getReceiver())) {
        throw new RuntimeException("邮件收信人不能为空");
    }
    if (StringUtils.isEmpty(mail.getSubject())) {
        throw new RuntimeException("邮件主题不能为空");
    }
    if (StringUtils.isEmpty(mail.getText()) && null == mail.getEmailTemplateContext()) {
        throw new RuntimeException("邮件内容不能为空");
    }
}

3.2 Сохранение электронной почты в базе данных

После отправки почта будет сохранена в базе данных, что удобно для статистики и отслеживания проблем с почтой.

private Mail saveMail(Mail mail) {
    // todo 发送成功/失败将邮件信息同步到数据库
    return mail;
}

3.3 Отправить письмо

  • Отправить текстовое письмо
public void sendSimpleMail(Mail mail){
    checkMail(mail);
    SimpleMailMessage mailMessage = new SimpleMailMessage();
    mailMessage.setFrom(sender);
    mailMessage.setTo(mail.getReceiver());
    mailMessage.setSubject(mail.getSubject());
    mailMessage.setText(mail.getText());
    mailSender.send(mailMessage);
    saveMail(mail);
}
  • Отправить письмо с вложениями
public void sendAttachmentsMail(Mail mail) throws MessagingException {
    checkMail(mail);
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    helper.setFrom(sender);
    helper.setTo(mail.getReceiver());
    helper.setSubject(mail.getSubject());
    helper.setText(mail.getText());
    File file = new File(mail.getFilePath());
    helper.addAttachment(file.getName(), file);
    mailSender.send(mimeMessage);
    saveMail(mail);
}
  • Отправить шаблон электронной почты
public void sendTemplateMail(Mail mail) throws MessagingException {
    checkMail(mail);
    // templateEngine 替换掉动态参数,生产出最后的html
    String emailContent = templateEngine.process(mail.getEmailTemplateName(), mail.getEmailTemplateContext());

    MimeMessage mimeMessage = mailSender.createMimeMessage();

    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    helper.setFrom(sender);
    helper.setTo(mail.getReceiver());
    helper.setSubject(mail.getSubject());
    helper.setText(emailContent, true);
    mailSender.send(mimeMessage);
    saveMail(mail);
}

В-четвертых, тестирование и оптимизация

4.1 Модульное тестирование

  1. При тестировании электронных писем с вложениями вложения помещаются вstaticпод папку;
  2. При тестировании шаблонных писем шаблон помещается вfileпод папку.
@RunWith(SpringRunner.class)
@SpringBootTest
public class MailServiceTest {

    @Resource
    MailService mailService;
    
    /**
     * 发送纯文本邮件
     */
    @Test
    public void sendSimpleMail() {
        Mail mail = new Mail();
//        mail.setReceiver("17098705205@163.com");
        mail.setReceiver("van93@qq.com");
        mail.setSubject("测试简单邮件");
        mail.setText("测试简单内容");
        mailService.sendSimpleMail(mail);
    }

    /**
     * 发送邮件并携带附件
     */
    @Test
    public void sendAttachmentsMail() throws MessagingException {
        Mail mail = new Mail();
//        mail.setReceiver("17098705205@163.com");
        mail.setReceiver("van93@qq.com");
        mail.setSubject("测试附件邮件");
        mail.setText("附件邮件内容");
        mail.setFilePath("file/dusty_blog.jpg");
        mailService.sendAttachmentsMail(mail);
    }

    /**
     * 测试模版邮件邮件
     */
    @Test
    public void sendTemplateMail() throws MessagingException {
        Mail mail = new Mail();
//        mail.setReceiver("17098705205@163.com");
        mail.setReceiver("van93@qq.com");
        mail.setSubject("测试模版邮件邮件");
        //创建模版正文
        Context context = new Context();
        // 设置模版需要更换的参数
        context.setVariable("verifyCode", "6666");
        mail.setEmailTemplateContext(context);
        // 模版名称(模版位置位于templates目录下)
        mail.setEmailTemplateName("emailTemplate");
        mailService.sendTemplateMail(mail);
    }
    
}

4.2 Оптимизация

Поскольку обычно существуют требования CC/BCC для отправки электронных писем, здесь сущность и класс инструмента инкапсулированы для облегчения прямых вызовов службы электронной почты.

  • Информация об электронной почте
@Data
public class MailDomain {
    /**
     * 邮件id
     */
    private String id;
    /**
     * 邮件发送人
     */
    private String sender;
    /**
     * 邮件接收人 (多个邮箱则用逗号","隔开)
     */
    private String receiver;
    /**
     * 邮件主题
     */
    private String subject;
    /**
     * 邮件内容
     */
    private String text;

    /**
     * 抄送(多个邮箱则用逗号","隔开)
     */
    private String cc;
    /**
     * 密送(多个邮箱则用逗号","隔开)
     */
    private String bcc;
    /**
     * 附件/文件地址
     */
    private String filePath;
    /**
     * 附件/文件名称
     */
    private String fileName;
    /**
     * 是否有附件(默认没有)
     */
    private Boolean isTemplate = false;
    /**
     * 模版名称
     */
    private String emailTemplateName;
    /**
     * 模版内容
     */
    private Context emailTemplateContext;
    /**
     * 发送时间(可指定未来发送时间)
     */
    private Date sentDate;
}
  • почтовый инструмент
@Component
public class EmailUtil {

    @Resource
    private JavaMailSender mailSender;

    @Resource
    TemplateEngine templateEngine;

    @Value("${spring.mail.username}")
    private String sender;

    /**
     * 构建复杂邮件信息类
     * @param mail
     * @throws MessagingException
     */
    public void sendMail(MailDomain mail) throws MessagingException {

        //true表示支持复杂类型
        MimeMessageHelper messageHelper = new MimeMessageHelper(mailSender.createMimeMessage(), true);
        //邮件发信人从配置项读取
        mail.setSender(sender);
        //邮件发信人
        messageHelper.setFrom(mail.getSender());
        //邮件收信人
        messageHelper.setTo(mail.getReceiver().split(","));
        //邮件主题
        messageHelper.setSubject(mail.getSubject());
        //邮件内容
        if (mail.getIsTemplate()) {
            // templateEngine 替换掉动态参数,生产出最后的html
            String emailContent = templateEngine.process(mail.getEmailTemplateName(), mail.getEmailTemplateContext());
            messageHelper.setText(emailContent, true);
        }else {
            messageHelper.setText(mail.getText());
        }
        //抄送
        if (!StringUtils.isEmpty(mail.getCc())) {
            messageHelper.setCc(mail.getCc().split(","));
        }
        //密送
        if (!StringUtils.isEmpty(mail.getBcc())) {
            messageHelper.setCc(mail.getBcc().split(","));
        }
        //添加邮件附件
        if (mail.getFilePath() != null) {
            File file = new File(mail.getFilePath());
            messageHelper.addAttachment(file.getName(), file);
        }
        //发送时间
        if (StringUtils.isEmpty(mail.getSentDate())) {
            messageHelper.setSentDate(mail.getSentDate());
        }
        //正式发送邮件
        mailSender.send(messageHelper.getMimeMessage());
    }

    /**
     * 检测邮件信息类
     * @param mail
     */
    private void checkMail(MailDomain mail) {
        if (StringUtils.isEmpty(mail.getReceiver())) {
            throw new RuntimeException("邮件收信人不能为空");
        }
        if (StringUtils.isEmpty(mail.getSubject())) {
            throw new RuntimeException("邮件主题不能为空");
        }
        if (StringUtils.isEmpty(mail.getText()) && null == mail.getEmailTemplateContext()) {
            throw new RuntimeException("邮件内容不能为空");
        }
    }

    /**
     * 将邮件保存到数据库
     * @param mail
     * @return
     */
    private MailDomain saveMail(MailDomain mail) {
        // todo 发送成功/失败将邮件信息同步到数据库
        return mail;
    }
}

Специальные тесты см.Пример кода на гитхабе, здесь не публикуется.

V. Резюме и дополнение

5.1 Асинхронная отправка

Во многих случаях отправка электронной почты не является тем результатом, на который должен обращать внимание наш основной бизнес. Например, уведомления и напоминания могут допускать задержки или сбои. В настоящее время для отправки электронных писем можно использовать асинхронный метод, чтобы ускорить выполнение основной транзакции.В реальных проектах его можно использоватьMQОтправить параметры, связанные с почтой, начать отправку почты после прослушивания очереди сообщений.

5.2 Ошибка отправки

По разным причинам всегда будут сбои при отправке электронных писем, например: электронные письма отправляются слишком часто, сбои в сети и т. д. Когда это происходит, мы обычно рассматриваем повторную попытку отправки электронной почты, которая будет реализована в следующих шагах:

  1. Получив запрос на отправку письма, сначала запишите запрос и поместите его в хранилище;
  2. Вызвать интерфейс отправки почты для отправки почты и записать результат отправки в базу данных;
  3. В течение периода сканирования системы синхронизации запуска передача не удалась, а количество попыток меньше3раз, и отправить его снова.

5.3 Другие вопросы

Проблемы с почтовым портом и размер вложения.

5.4 Образец кода адреса

5.5 Технический обмен

  1. Пыль Блог
  2. Блог Пыли - Самородки
  3. Пыль Блог - Блог Парк
  4. Github
  5. публика
    风尘博客