发邮件,项目的必备功能之一,如果一个稍微模块化一点的公司,一般会单独出来一个项目专用来做公司的发送信息的功能,当然这个发送信息中不止包含发邮件,还会有短信、APP push等。这篇聊聊推送邮件。
在以前的开发中,公司用Java mail的比较多,由自己来写邮件的组装和发送功能,但是Java mail使用操作比较繁杂,后来渐渐的都开始使用spring提供的JavaMailSender工具来实现,用过的都知道,这叫一个爽,执行几个set、add操作,一个复杂的邮件就可以发送出去,但是在业务代码中发送邮件的位置很多,不能在每个业务逻辑位置都写一遍同样的set操作,这是基本常识,因此都会基于JavaMailSender再做一个工具类,这个工具类让调用者只用一句话实现就OK。引出主题工具类,接下来就开干吧。
这边的主要内容是写JavaMailSender,但是开始还是来看看JavaMail的基本实现,因为JavaMailSender也是基于JavaMail做的再次封装。
发送邮件的三个重点:
Session中实际也提供了获取transport的方法,但是这个transport不好用,还是习惯用Transport提供的静态调用。
/*Transport静态方法直接调用,将message传入即可*/
Transport.send(message);
/*Session获取transport方式调用*/
Transport transport = session.getTransport();
//address需要手动添加,表示发送人
transport.sendMessage(message,new Address[1]);
上面Session的transport只能指定发送人,抄送人、密送人都不能添加,如果在MimeMessage中添加不知道能不能起作用,这个没有尝试过,如果你喜欢用这种方式可以分享一下。
Java mail有两个依赖,分别是mail和activation。
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
简单的发送邮件没有复杂的逻辑,代码也很清晰,直接上代码:
private static void send() throws MessagingException {
//构建Session
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.163.com");
properties.setProperty("mail.transport.protocol","smtp");
properties.setProperty("mail.smtp.auth","true");
properties.setProperty("mail.host.port","25");
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("xxx@163.com","xxxxxx");
}
});
session.setDebug(true);
//构建邮件MimeMessage
MimeMessage message = new MimeMessage(session);
message.setSubject("这是一封测试邮件!");
message.setText("测试邮件内容随意");
message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com"));
message.setFrom(new InternetAddress("xxx@163.com"));
//发送邮件
Transport.send(message);
}
需要注意的点:
com.sun.mail.smtp.SMTPSendFailedException: 553 Mail from must equal authorized user
;setRecipient
方法,在第一参数中指定抄送类型即可。复杂邮件使用的相对会比较多一点,在发送邮件的时候多少会带上内嵌图片或者附件。MimeMessage是整个邮件本体,构建复杂邮件的过程就是向这个本体中加不同的内容,Java mail提供了一个类MimeBodyPart
,表示邮件内容的组件。将需要发送的内容用组件包装起来,再塞到邮件本地中即可。
示例代码:
private static void send() throws MessagingException {
//构建Session
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.163.com");
properties.setProperty("mail.transport.protocol","smtp");
properties.setProperty("mail.smtp.auth","true");
properties.setProperty("mail.host.port","25");
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("xxx@163.com","xxxxx");
}
});
session.setDebug(true);//打印发送日志
//构建基础信息
MimeMessage message = new MimeMessage(session);
message.setSubject("这是一封测试邮件!");
//message.setText("测试邮件内容随意");
message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com"));
message.setFrom(new InternetAddress("xxx@163.com"));
//构建内容
MimeBodyPart text = new MimeBodyPart();
text.setContent("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", "text/html;charset=UTF-8");
MimeBodyPart bodyPart = new MimeBodyPart();
//构建内嵌图片
DataHandler dataHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png"));
bodyPart.setDataHandler(dataHandler);
bodyPart.setContentID("avatar");//如果没有这句,表示作为附件发送,加上表示显示在邮件内容的一部分
//构建附件
MimeBodyPart attachment = new MimeBodyPart();
DataHandler attachmentHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png"));
attachment.setDataHandler(attachmentHandler);
attachment.addHeader("Content-Type","UTF-8");//防止中文名乱码
attachment.setFileName("avatar.png");//这里名称记得带上后缀名,否则邮件附件下载下来需要手动添加后缀,可能会损坏
//构建邮件体内容
MimeMultipart mimeMultipart = new MimeMultipart();
mimeMultipart.addBodyPart(text);
mimeMultipart.addBodyPart(bodyPart);
mimeMultipart.addBodyPart(attachment);
mimeMultipart.setSubType("mixed");//设置为mixed,别忘了哦
message.setContent(mimeMultipart);
//发送邮件
Transport.send(message);
}
复杂邮件构建的过程不是很难,就是代码量会多一点。
JavaMailSender是由spring提供的,功能很强大,对Java mail做了一层封装,在Spring boot中可以拿来用,是自动集成的。
和Spring boot整合后,引入依赖就很简单啦。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--是在使用邮件模板的时候使用,不使用模板可以忽略-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.6.4</version>
</dependency>
使用JavaMailSender需要在配置文件中配置相关的信息,在Java mail中是通过Session来配置,在JavaMailSender上,在Spring项目中可以通过在配置文件中配置。(如果使用的是spring boot,在properties中配置,如果是Spring MVC项目就要用xml文件啦,或者自己手动的使用Configuration类来配置,本文以Spring boot为基础)
spring.mail.default-encoding=utf-8
spring.mail.host=smtp.163.com
spring.mail.protocol=smtp
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.transport.protocol=smtp
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.username=xxx@163.com
spring.mail.password=xxxx
简单邮件发送方法,到底简单到什么程度,看下面的代码:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ItcrudCommonsUtilApplication.class)
public class JavaMailSenderTest {
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private VelocityEngine velocityEngine;//暂时用不上,在下面说到模板邮件使用
@Test
public void sendTest() throws MessagingException {
//创建邮件本体
MimeMessage message = javaMailSender.createMimeMessage();
//创建本体编辑的协助者(YY出来的名字)
MimeMessageHelper helper = new MimeMessageHelper(message);
//构建邮件本体
helper.setSubject("邮件标题");
helper.setText("发送的内容");
helper.setTo("itcrud@aliyun.com");
helper.setFrom("xxx@163.com");
//发送邮件
javaMailSender.send(helper.getMimeMessage());
}
}
看了这段发送邮件的代码,然后和上面Java mail的代码对比,果断的舍弃Java mail的有木有。
对于Java mail来说是复杂邮件,但是对于JavaMailSender来说,复杂也会变得简单。
示例代码:
@Test
public void sendComplexText() throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
//构建邮件本体内容
helper.setSubject("复杂邮件");
helper.setFrom("xxx@163.com");
helper.setTo("itcrud@aliyun.com");
helper.setText("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", true);
helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png"));
helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png"));
//发送邮件
javaMailSender.send(helper.getMimeMessage());
}
代码的减少量可以看得出来吧。那些复杂的封装逻辑就交给Spring来处理啦。
需要注意的两点:(刚开始用的时候被坑过)
上面的两个内容都不是最主要的,在将Java mail的时候最后一个功能很难说,代码量太多。下面我们用JavaMailSender来实现。
场景:在公司对外发送邮件的时候,除了附件、内嵌图片和普通文本,还有两个很重的东西,那就是签名和占位填充。个人发邮件的时候都会带上自己的签名,何况是公司向外发邮件啦。另外一个邮件的基本格式相同,只是部分填充内容不同,总不能每次发送文件都要重新写一个html文件吧。
但是想想,邮件签名就直接加在内容里面不就可以啦,然后使用html格式来进行邮件的排版。对的没有错,但是如果整个邮件的排版比较复杂,html内容很长怎么办。那也没问题啊,可以放在文件里面,通过流来读取文件不就可以了吗。对的的确是这样的,但是如果使用Java mail就要去手动的读取问题,不觉得会很麻烦吗。还有就是占位符的填充。
这个时候JavaMailSender帮我们解决了这一系列的问题。准确的说是和另一个插件Velocity结合来解决。这就是在引入依赖的时候,我加上velocity的依赖的重点所在。
只需要两步就可以搞定:
<!--模板文件的内容-->
<html>
<body>
<h1>这是邮件的标题</h1>
<div>
<p>很长很长的邮件内容上半部分</p>
<p>很长很长的邮件内容上半部分</p>
</div>
<img src="cid:avatar">
<div>
<p>很长很长的邮件内容下半部分</p>
<p>很长很长的邮件内容下半部分</p>
<p>还有一部分内容看附件</p>
</div>
<div>
<p>
这里是签名的内容
公司名称:xxxx有限责任公司
联系电话:xxxxxxxx
</p>
</div>
</body>
</html>
@Test
public void sendSuperTest() throws MessagingException {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
//构建邮件本体内容
helper.setSubject("复杂邮件");
helper.setFrom("xxx@163.com");
helper.setTo("itcrud@aliyun.com");
//使用
VelocityContext context = new VelocityContext();
context.put("content","占位符需要填充的内容");
try (StringWriter writer = new StringWriter()) {
velocityEngine.mergeTemplate("/mail/temp.vm", "UTF-8", context, writer);
helper.setText(writer.toString(), true);
} catch (Exception e) {
e.printStackTrace();
}
helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png"));
helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png"));
//发送邮件
javaMailSender.send(helper.getMimeMessage());
}
完美解决上面的两个问题,并且整个实现过程的代码量也是很少的,比上面Java mail发送一个复杂邮件还要少。
这里讲到了Java mail发送邮件和Spring封装后的JavaMailSender,两者对比来说,JavaMailSender要比Java mail直接实现要简单的多。如果你有时间其实可以自己实现一套Spring的这种封装,但是为何要重复造轮子呢。