以前发送邮件可能大家需要使用javax下的mail包,自己创建session,mimemessage这种;其实spring帮我们集成了一下,让发邮件变的更简单、可靠。
1.引入对应依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.具体使用
首先spring 的依赖常规使用方式就是配置加注入,邮件模块也不例外。先来看看配置
spring:
mail:
host: 10.110.xxx.xxx #邮件服务器地址
port: 25 #邮件服务器端口
protocol: smtp #使用的协议
default-encoding: UTF-8 #默认编码
username: user #这个是通过邮件服务器认证的用户名和密码,不一定是邮箱,看服务器的要求
password: password
properties: #properties中的属性都是比较灵活可配置的,其实是javax.mail.Session中对应的配置项,可以参考对应文档
mail.smtp.auth: true #如果邮件服务器需要实名需要认证开启此选项
mail.from: 100010@qq.com #统一设置发件人邮箱
然后放具体代码,后面一点一点分析
邮件实体类
import lombok.*;
import org.springframework.core.io.InputStreamSource;
import javax.activation.DataSource;
import javax.mail.internet.InternetAddress;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author
* @Description
* @create 2022-07-12 14:23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MailInfo {
/**
* 邮件标题
*/
private String subject;
/**
* 邮件正文
*/
private String content;
/**
* 发件时间,默认当前时间
*/
private Date sentDate;
/**
* 收件人
*/
private List<String> recipients;
/**
* 抄送人
*/
private List<String> ccs;
/**
* 密件抄送人
*/
private List<String> bccs;
/**
* 是否为邮件组
*/
private Boolean isGroup;
/**
* 文件形式附件
*/
@Setter(AccessLevel.NONE)
private Map<String, byte[]> attachments = new HashMap<>();
public void addAttachment(String fileName, byte[] file){ attachments.put(fileName, file); }
}
service层实现
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.cicc.op.mail.api.MailService;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.util.ByteArrayDataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* @author
* @Description
* @create 2022-07-26 15:39
*/
@Slf4j
@Service
public class MailController {
@Autowired
private JavaMailSender javaMailSender;
@Override
public MailResponse sendMail(MailInfo mailInfo) {
Long id = recordLog(mailInfo);
log.info("----------开始邮件发送------------");
checkValue(mailInfo, id);
//html加附件邮件发送
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, true);
if(mailInfo.getIsGroup() != null && mailInfo.getIsGroup()){
List<InternetAddress> internetAddressList = new ArrayList<>();
mailInfo.getRecipients().forEach(to ->{
try {
internetAddressList.add(new InternetAddress(to, true));
} catch (AddressException e) {
log.error("邮件发送失败,地址信息有误", e);
throw new CommonMailException("邮件发送失败,地址信息有误", id, e);
}
});
mimeMessageHelper.setTo(internetAddressList.toArray(new InternetAddress[internetAddressList.size()]));
}else {
mimeMessageHelper.setTo(mailInfo.getRecipients().toArray(new String[mailInfo.getRecipients().size()]));
}
mimeMessageHelper.setSubject(mailInfo.getSubject());
mimeMessageHelper.setText(mailInfo.getContent(), true);
if (mailInfo.getAttachments() != null && !mailInfo.getAttachments().isEmpty()) {
for (String fileName : mailInfo.getAttachments().keySet()) {
byte[] file = mailInfo.getAttachments().get(fileName);
if(file != null){
mimeMessageHelper.addAttachment(fileName, new ByteArrayResource(file));
} else {
throw new CommonMailException("不支持的附件类型", id);
}
}
}
if (mailInfo.getSentDate() != null) {
mimeMessageHelper.setSentDate(mailInfo.getSentDate());
}
if (mailInfo.getCcs() != null && !mailInfo.getCcs().isEmpty()) {
for (String cc : mailInfo.getCcs()) {
mimeMessageHelper.setCc(cc);
}
}
if (mailInfo.getBccs() != null && !mailInfo.getBccs().isEmpty()) {
for (String bcc : mailInfo.getBccs()) {
mimeMessageHelper.setBcc(bcc);
}
}
javaMailSender.send(message);
} catch (MessagingException e) {
log.error("邮件发送失败", e);
throw new CommonMailException("邮件发送失败", id, e);
}
return new MailResponse(true, "发送成功");
}
private void checkValue(MailInfo mailInfo, Long id) {
if (!StringUtils.hasText(mailInfo.getSubject())) {
throw new CommonMailException("邮件标题不能为空", id);
} else if (!StringUtils.hasText(mailInfo.getContent())) {
throw new CommonMailException("邮件内容不能为空", id);
} else if (mailInfo.getRecipients().isEmpty()) {
throw new CommonMailException("收件人不能为空", id);
}
}
}
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;
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, boolean multipart) throws MessagingException {
this(mimeMessage, multipart, (String)null);
}
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, @Nullable String encoding) throws MessagingException {
this(mimeMessage, multipart ? 3 : 0, encoding);
}
public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode) throws MessagingException {
this(mimeMessage, multipartMode, (String)null);
}
public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, @Nullable String encoding) throws MessagingException {
this.encodeFilenames = false;
this.validateAddresses = false;
this.mimeMessage = mimeMessage;
this.createMimeMultiparts(mimeMessage, multipartMode);
this.encoding = encoding != null ? encoding : this.getDefaultEncoding(mimeMessage);
this.fileTypeMap = this.getDefaultFileTypeMap(mimeMessage);
}
使用MimeMessageHelper的时候注意三个点,
-
一个是multipart字段,如果要进行附件传输的话,这个要设置为true,还有就是如果配置文件中没有设置默认字符集,然后发送邮件出现乱码情况,可以在encoding设置编码。
-
还有一个是setText方法,如果需要发送的邮件正文不是简单的文本格式,而是使用了模板或者有图片表格等内容,需要在setText方法设置strict属性为true。
-
邮件的附件如果需要http请求进行传输,但是又不方便使用Multipartfile类型,可以使用byte数组发送,如果是JSON需要注意序列化时编码问题;直接使用form-data也可以。