Java中邮件发送

330 阅读4分钟

spring已经集成了邮件模块,使用JavaMailSender进行邮件发送,让发邮件变的更简单、可靠。

1.引入对应依赖

*build.gradle*
dependencies {
    implementation "org.springframework.boot:spring-boot-starter-mail"
}

*pom.xml*
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2.yaml配置

spring:
	mail:
    	host: xxx   	       #邮件服务器地址
    	port: 80               #邮件服务器端口
    	protocol: smtp         #使用的协议
    	default-encoding: UTF-8 #默认编码
    	username: user         #这个是通过邮件服务器认证的用户名和密码,不一定是邮箱,看服务器的要求
    	password: password
    	properties:            #properties中的属性都是比较灵活可配置的,其实是javax.mail.Session中对应的配置项,可以参考对应文档
      		mail:
			 from: 1@qq.com         #统一设置发件人邮箱
			 smtp:
				auth: true      #如果邮件服务器需要实名需要认证开启此选项
				starttls:
					enable: true
					required: false
				socketFactory:
					class: javax.net.ssl.SSLSocketFactory
		default-encoding: utf-8

定义邮件实体类

/**
 * 邮件实体类
 */
@Getter
@Setter
@ToString
public class MimeEmail implements Serializable {

    private static final long serialVersionUID = 4006574481733672739L;

    /**
     * 主题
     */
    private String subject;

    /**
     * 主题内容
     */
    private String content;

    /**
     * html主题内容
     */
    private String html;

    /**
     * 收件人, 多个邮箱以','分隔, example = "xx@test.com,zz@test.com"
     */
    private String receivers;

    /**
     * 抄送人, 多个邮箱以','分隔, example = "xx@test.com,zz@test.com"
     */
    private String cc;

    /**
     * 附件名字
     */
    private String attachmentName;

    /**
     * 获取附件的输入流
     */
    private transient InputStreamSource inputStreamSource;
}

邮件发送

//发送邮件
MimeEmail mimeEmail = new MimeEmail();
mimeEmail.setSubject(param.getTitle());
mimeEmail.setContent(StrUtil.format(param.getContent(), cn.hutool.core.date.DateUtil.format(new Date(),
        YYYY_MM_DD_HH_MM)));
mimeEmail.setReceivers(param.getEmail());
mimeEmail.setCc(param.getCc());
try {
    log.info("邮件参数:{}", mimeEmail.toString());
    mailService.sendHtmlMails(mimeEmail, map);
} catch (Exception e) {
    log.error("send email exception:{}", e.getMessage(), e);
}

service层实现

@Service
@Slf4j
public class MailService {

    @Resource
    private JavaMailSender javaMailSender;
    @Value("${spring.mail.username}")
    private String from;

    public void sendHtmlMail(MimeEmail mimeEmail) throws Exception {

        //创建一个MINE消息
        MimeMessage message = javaMailSender.createMimeMessage();

        MimeMessageHelper mineHelper;
        try {
            //创建一个MimeMessageHelper对象
            mineHelper = new MimeMessageHelper(message, true);
            //谁发
            mineHelper.setFrom(from);
            log.info("==========="+from+"============");
            //谁要接收
            mineHelper.setTo(mimeEmail.getReceivers().split(CommonConstant.COMMENT_SPLIT));
            //抄送人
            if (mimeEmail.getCc() != null && !"".equals(mimeEmail.getCc())) {
                mineHelper.setCc(mimeEmail.getCc().split(CommonConstant.COMMENT_SPLIT));
            }
            //邮件主题
            mineHelper.setSubject(mimeEmail.getSubject());
            //邮件内容   true 表示带有附件或html
            mineHelper.setText(mimeEmail.getContent(), true);
            if (mimeEmail.getAttachmentName()!=null && !ObjectUtil.isEmpty(
                    mimeEmail.getInputStreamSource())) {
                //添加附件
                mineHelper.addAttachment(mimeEmail.getAttachmentName(), new ByteArrayResource(
                        IOUtils.toByteArray(mimeEmail.getInputStreamSource().getInputStream())));
            }
        } catch (IOException | MessagingException e) {
            throw new Exception("init message helper exception", e);
        }
        javaMailSender.send(message);
    }


    /**
     * 发送多个附件
     * @param mimeEmail
     * @throws Exception
     */
    public void sendHtmlMails(MimeEmail mimeEmail, Map<String, InputStreamSource> map) throws Exception {

        //创建一个MINE消息
        MimeMessage message = javaMailSender.createMimeMessage();

        MimeMessageHelper mineHelper;
        try {
            //创建一个MimeMessageHelper对象
            mineHelper = new MimeMessageHelper(message, true);
            //谁发
            mineHelper.setFrom(from);
            log.info("==========="+from+"============");
            //谁要接收
            mineHelper.setTo(mimeEmail.getReceivers().split(CommonConstant.COMMENT_SPLIT));
            //抄送人
            if (mimeEmail.getCc() != null && !"".equals(mimeEmail.getCc())) {
                mineHelper.setCc(mimeEmail.getCc().split(CommonConstant.COMMENT_SPLIT));
            }
            //邮件主题
            mineHelper.setSubject(mimeEmail.getSubject());
            //邮件内容   true 表示带有附件或html
            mineHelper.setText(mimeEmail.getContent(), true);
            if(map.size() > 0) {
                for (String attachmentName : map.keySet()) {
                    //添加附件
                    mineHelper.addAttachment(attachmentName, new ByteArrayResource(
                            IOUtils.toByteArray(map.get(attachmentName).getInputStream())));
                }
            }
        } catch (IOException | MessagingException e) {
            throw new Exception("init message helper exception", e);
        }
        javaMailSender.send(message);
    }
}

3.深入了解JavaMailSender

只要配置好spring.mail相关,就可以直接注入此Bean。


package org.springframework.mail.javamail;

import java.io.InputStream;

import javax.mail.internet.MimeMessage;

import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;

/**
 * Extended {@link org.springframework.mail.MailSender} interface for JavaMail,
 * supporting MIME messages both as direct arguments and through preparation
 * callbacks. Typically used in conjunction with the {@link MimeMessageHelper}
 * class for convenient creation of JavaMail {@link MimeMessage MimeMessages},
 * including attachments etc.
 *
 * <p>Clients should talk to the mail sender through this interface if they need
 * mail functionality beyond {@link org.springframework.mail.SimpleMailMessage}.
 * The production implementation is {@link JavaMailSenderImpl}; for testing,
 * mocks can be created based on this interface. Clients will typically receive
 * the JavaMailSender reference through dependency injection.
 *
 * <p>The recommended way of using this interface is the {@link MimeMessagePreparator}
 * mechanism, possibly using a {@link MimeMessageHelper} for populating the message.
 * See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
 *
 * <p>The entire JavaMail {@link javax.mail.Session} management is abstracted
 * by the JavaMailSender. Client code should not deal with a Session in any way,
 * rather leave the entire JavaMail configuration and resource handling to the
 * JavaMailSender implementation. This also increases testability.
 *
 * <p>A JavaMailSender client is not as easy to test as a plain
 * {@link org.springframework.mail.MailSender} client, but still straightforward
 * compared to traditional JavaMail code: Just let {@link #createMimeMessage()}
 * return a plain {@link MimeMessage} created with a
 * {@code Session.getInstance(new Properties())} call, and check the passed-in
 * messages in your mock implementations of the various {@code send} methods.
 *
 * @author Juergen Hoeller
 * @since 07.10.2003
 * @see javax.mail.internet.MimeMessage
 * @see javax.mail.Session
 * @see JavaMailSenderImpl
 * @see MimeMessagePreparator
 * @see MimeMessageHelper
 */
public interface JavaMailSender extends MailSender {

   MimeMessage createMimeMessage();

   MimeMessage createMimeMessage(InputStream contentStream) throws MailException;

   void send(MimeMessage mimeMessage) throws MailException;

   void send(MimeMessage... mimeMessages) throws MailException;

   void send(MimeMessagePreparator mimeMessagePreparator) throws MailException;
   
   void send(MimeMessagePreparator... mimeMessagePreparators) throws MailException;

}

接口里面就两个环节,创建MimeMessage对象(里面是邮件相关属性),然后send发送。 实现类JavaMailSenderImpl

public synchronized Session getSession() {
        if (this.session == null) {
            this.session = Session.getInstance(this.javaMailProperties);
        }
 
        return this.session;
    }
 
 
public MimeMessage createMimeMessage() {
        return new SmartMimeMessage(this.getSession(), this.getDefaultEncoding(), this.getDefaultFileTypeMap());
    }
 
    public MimeMessage createMimeMessage(InputStream contentStream) throws MailException {
        try {
            return new MimeMessage(this.getSession(), contentStream);
        } catch (Exception var3) {
            throw new MailParseException("Could not parse raw MIME content", var3);
        }
    }

getSession方法可以看出,spring是对javax实现的邮件发送进行了一层封装,还是用的session,之前的properties配置也是在这里给到session,然后通过session创建MimeMessage。直接操作MimeMessage写入也可以,但是一个是不太方便,一个是不能发送附件,所以借助MimeMessageHelper来实现。

public MimeMessageHelper(MimeMessage mimeMessage) {
   this(mimeMessage, null);
}

public MimeMessageHelper(MimeMessage mimeMessage, @Nullable String encoding) {
   this.mimeMessage = mimeMessage;
   this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage));
   this.fileTypeMap = getDefaultFileTypeMap(mimeMessage);
}

public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException {
   this(mimeMessage, multipart, null);
}

public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, @Nullable String encoding)
      throws MessagingException {
   this(mimeMessage, (multipart ? MULTIPART_MODE_MIXED_RELATED : MULTIPART_MODE_NO), encoding);
}

public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode) throws MessagingException {
   this(mimeMessage, multipartMode, null);
}

public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, @Nullable String encoding)
      throws MessagingException {
   this.mimeMessage = mimeMessage;
   createMimeMultiparts(mimeMessage, multipartMode);
   this.encoding = (encoding != null ? encoding : getDefaultEncoding(mimeMessage));
   this.fileTypeMap = getDefaultFileTypeMap(mimeMessage);
}

使用MimeMessageHelper注意点:

  1. 一是multipart字段,如果要进行附件传输的话,这个要设置为true,如果配置文件中没有设置默认字符集,然后发送邮件出现乱码情况,可以在encoding设置编码。
  2. 还有setText方法,如果需要发送的邮件正文不是简单的文本格式,而是使用了模板或者有图片表格等内容,需要在setText方法设置strict属性为true。
  3. 邮件的附件如果需要http请求进行传输,但是又不方便使用Multipartfile类型,可以使用byte数组发送,如果是JSON需要注意序列化时编码问题;直接使用form-data也可以。