Java实现Email发送邮件


一.电子邮件协议:

SMTP 简单邮件传输

SMTP是Simple Mail Transfer Protocol的简称,即简单邮件传输协议。该协议定义了邮件客户端软件和SMTP服务器之间,以及两台SMTP服务器之间的通信规则。在Java Mail API中,基于Java Mail的程序将与本公司或Internet服务提供商(ISP)的SMTP服务器通信,该SMTP服务器将会把消息转发给用于接收消息的SMTP服务器,最后用户可通过POP或IMAP协议获取该消息。由于支持身份验证,所以不需要SMTP服务器是一种开放的转发器,但需要确保SMTP服务器配置正确。Java Mail API中没有集成用于处理配置服务器以转发消息或添加/删除电子邮件账户等功能。

POP3邮局协议

POP3是Post Office Protocol 3的简称,即邮局协议第3版,该协议是在RFC 1939中定义的。它是Internet电子邮件的第一个离线协议标准,也就是说,POP3是一个简单而实用的邮件信息传输协议。目前在Internet上,大多数人都采用POP3来接收邮件。POP3为每个用户的每个邮箱提供支持,但是POP3并不支持查看邮件信息及统计新邮件的功能,因此,在使用Java Mail API时,如果想获取这些信息,需要自己进行计算。

IMAP 接收邮件协议

IMA P的含义是Internet消息访问协议,当前版本是第4版,也称作IMAP4,该协议是在RFC 2060中定义的。IMAP是接收消息的更加高级协议。使用IMAP时,所用邮件服务器必须支持该协议,但是如果所有邮件服务器只支持该协议,并不能使用全部IMAP协议,为了能够支持全部IMAP协议,所用邮件服务器还应该支持POP协议。在Java Mail程序通过IMAP协议可以在服务器上拥有包括多个文件夹的用户,并且这些文件夹可以被多个用户共享。虽然IMAP协议具有更高级的功能,但是也不要让每个用户都使用,这是因为IMAP需要服务器接收新消息,发送消息给请求的用户,并在多个文件夹中为每个用户维护消息、备份信息,这会加重邮件服务器的负荷。

MIME

MIME是多用途的网际邮件扩充协议的简称。它不是一种邮件传输协议,而是用于定义传输内容(如消息的格式、附件等)的协议。许多文档都对MIME协议作了描述(包括RFC 2045、RFC 2046和RFC 2047)。MIME是对RFC 822的扩充,RFC 822规定了内容只包括采用ASCII编码的纯文本的邮件格式,而MIME允许在邮件中包含附件。作为Java Mail API的用户,一般不需要担心这些格式。

二.采用POP3/SMTP服务发送邮件

 1.配置依赖



    org.springframework.boot
    spring-boot-starter-mail

2.在application.yml进行配置

spring:
mail:
  # 使用的编码
  default-encoding: utf-8
  # 邮件协议smtp
  host: smtp.qq.com
  # 端口(默认25)
  port: 25
  # 发送者的邮件的用户名
  username: 2891434522@qq.com
  # 移动端客户授权码(在邮箱中设置)
  password: xxxxxxxxxxxxx

3.发送邮件,同时兼容简单邮件和带附件的邮件(附件可以是文件和图片,可以多个)

Controller控制层:

import com.example.service.IEmailService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.Objects;

@Controller
public class EmailController {

    @Resource
    private IEmailService emailService;

    /**
     * 发送邮件接口
     * @param file 附件数组
     * @param to 邮件接收方
     * @param subject 邮件主题
     * @param contentText 邮件内容
     * @return
     */
    @RequestMapping("sendEmail")
    @ResponseBody
    public boolean sendEmail(@RequestParam("file") MultipartFile[] file, String to, String subject, String contentText) {
        if (Objects.nonNull(file) && file.length > 0) {
            return emailService.sendAttachmentMail(to, subject, contentText, file);
        }
        return emailService.sendAttachmentMail(to, subject, contentText);
    }

}

4.新建IEmailService接口,包含简单邮件,和带附件接口

package com.example.service;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public interface IEmailService {
    /**
     * 
    * @Description 发送简单纯文字邮件
    * @param to 收件人地址
    * @param subject 邮件主题
    * @param content 邮件内容
* @return boolean 成功返回true,失败返回false
*/ boolean sendAttachmentMail(String to, String subject, String content); /** * * @Description 发送带单个或多个附件的邮件 * @param to 收件人地址 * @param subject 邮件主题 * @param content 邮件内容 * @param file 附件数组 * @return boolean 功返回true,失败返回false */ boolean sendAttachmentMail(String to, String subject, String content, MultipartFile[] file); }

5.新建EmailService实现类,实现IEmailService接口。

import com.example.service.IEmailService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Controller;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import java.util.Objects;

@Controller
public class EmailServiceImpl implements IEmailService {

    // 从application.properties配置文件中注入发送者的邮件地址
    @Value("${spring.mail.username}")
    private String fromEmail;
    @Value("${spring.mail.default-encoding}")
    private String encoding;

    //注入spring发送邮件的对象
    @Resource
    private JavaMailSender javaMailSender;

    @Override
    public boolean sendAttachmentMail(String to, String subject, String content) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setTo(to);
        simpleMailMessage.setFrom(fromEmail);
        simpleMailMessage.setSubject(subject);
        simpleMailMessage.setText(content);
        try {
            //执行发送
            javaMailSender.send(simpleMailMessage);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public boolean sendAttachmentMail(String to, String subject, String content, MultipartFile[] file) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        try {
            // 设置参数 不允许截断文件名
            System.setProperty("mail.mime.splitlongparameters", "false");
            // 指定 UTF-8 编码
            final MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, encoding);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content);
            helper.setFrom(fromEmail);
            if (Objects.nonNull(file) && file.length > 0) {
                //读取附件文件(传入文件路径)
                for (MultipartFile tempFile : file) {
                    String fileName = tempFile.getOriginalFilename();
                    // 编码文件名称
                    helper.addAttachment(MimeUtility.encodeWord(fileName),tempFile);
                }
                //发送邮件
                javaMailSender.send(mimeMessage);
            } else {
                //没有附件文件(中断发送)
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //成功发送
        return true;
    }

}

6.测试

三.附件名乱码

邮件里的附件名称中文却显示成了=UTF-8Q=E3=80=90...=UTF-8Q(2).docx=

因为编码后文件名太长被截断成多个属性名,而邮件接收端没有很好地支持这种方式,才导致乱码的。

解决方案

将系统参数mail.mime.splitlongparameters改成false即可解决这个问题,完整的代码如下

// 设置参数 不允许截断文件名
System.setProperty("mail.mime.splitlongparameters", "false");
final MimeMessage message = mailSender.createMimeMessage();
// 指定 UTF-8 编码
final MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "UTF-8");
messageHelper.setFrom(properties.getUsername(), SEND_ALIAS);
messageHelper.setSubject(title);
messageHelper.setText(subject, true);
// 编码文件名称
messageHelper.addAttachment(MimeUtility.encodeWord(file.getName()), fileSystemResource);

这样就可以解决附件中文名称乱码问题。