JavaMail的使用详解,看完真的明白了
总体介绍:
公司近期接到需求,由于安卓和IOS客户端在转发邮件时会重新下载一遍老邮件里附件,这个过程比较耗时,体验不佳,现在需要实现客户端走服务器来转发,免去下载的过程
我们先了解一下两种协议格式:
SMTP:邮件发送协议 ssl对应端口465 非ssl对应端口25
IMAP:收邮件协议 ssl对应端口993 非ssl对应端口143
以上描述的是邮箱服务器的默认端口 可自定义或变更 但主流端口就是以上这么几个
附上JavaMail官网地址,遇到问题时在里面会发现很多灵感:
https://javaee.github.io/javamail/docs/api/
maven引用
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
JavaMail邮件格式介绍
如果想了解邮件如何发送,我们必须了解邮件消息的正文结构
我们先了解一下发邮件时的流程:
//创建消息对象
MimeMessage message = new MimeMessage(session);
//创建消息正文
MimeMultipart mimmultiPart = new MimeMultipart();
//创建消息体
MimeBodyPart mimeBodyPart = new MimeBodyPart();
//将消息体包裹在正文中
mimmultiPart.addBodyPart(mimeBodyPart);
//将正文包裹在消息对象中
message.setContent(mimmultiPart )
//发邮件协议是smtp 收邮件协议是imap
Transport transport = session.getTransport("smtp");
//发送邮件
transport.sendMessage(mimeMessage,"某收件人");
MimeMessage 包含了 MimeMultipart,
MimeMultipart 包含了 MimeBodyPart
这里面比较绝妙的就是
MimeBodyPart 其实反过来也可以包含MimeMultipart 后面我们将看到
以下4种格式均为笔者通过FoxMail发送邮件,并通过JavaMail去找邮件而来的结果
①纯文本
邮件正文仅带有文本文字
--- MimeMutiPart("ALTERNATIVE")
MimeBodyPart0("TEXT/PLAIN")
MimeBodyPart1("TEXT/HTML")
使用FoxMail发邮件时,尽管是最简单的文本,也是一个MimeMutiPart包含了两个MimeBodyPart,一个对应纯文本,另一个对应html
②纯文本带附件
邮件正文包含文本文字+附件
--- MimeMutiPart("MIXED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1 (""APPLICATION/OCTET-STREAM")
文本的内容仍然是一个MutiPart包含了两个BodyPart 只不过此部分被包含进了MimeBodyPart0
附件的部分是一个独立的MimeBodyPart1
③富文本
邮件正文包含富文本文字
--- MimeMutiPart("RELATED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1("IMAGE/GIF")
|------MimeBodyPart2("IMAGE/GIF")
|------MimeBodyPart3("IMAGE/GIF")
文本的内容还是相同的,一个MutiPart包含了两个BodyPart 此MutiPart被包含进了
MimeBodyPart0
MimeBodyPart1、MimeBodyPart2、MimeBodyPart3属于"INLINE"的附件,而非attach的附件属于富文本中的图片和表情等资源
④富文本带附件
邮件正文包含富文本文字+附件
--- MimeMutiPart("MIXED")
|------MimeBodyPart0 ---- MimeMutiPart("RELATED")
| MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
| MimeBodyPart1("IMAGE/GIF")
| MimeBodyPart2("IMAGE/GIF")
| MimeBodyPart3("IMAGE/GIF")
|------MimeBodyPart1 ("APPLICATION/OCTET-STREAM")
|------MimeBodyPart2 ("APPLICATION/OCTET-STREAM")
|------MimeBodyPart3 ("APPLICATION/OCTET-STREAM")
这是最复杂的一种,也是可以基本满足我们日常需求的一种,最里面的一层还是ALTERNATIVE的MutiPart包含了两个文本类型,同层级包含着"INLINE"的附件
最外层是一些真正的附件
以上四种类型基本上涵盖了我们日常使用的格式,不难发现MimeMutiPart一共有三种类型:
1.MIXED:包含附件的文档
2.ALTERNATIVE:
包含text文档和html文档,为什么是两种 一般邮箱客户端比如FoxMail 可以切换阅读模式
一个是超文本阅读格式 对应html
一个是纯文本(去掉文中图片和表情)格式 对应text
3.RELATED:图文(富文本)
发送邮件代码样例
首先,邮箱服务都是基于Session会话开始,首先我们构建一个Session
public final static String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
public final static String DEFAULT_FACTORY = "javax.net.DefaultSocketFactory";
public static Session generSession(Dto_MainForward dto_mainForward,MailServers mailinfo) throws NoSuchProviderException, GeneralSecurityException {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
MailSSLSocketFactory socketFactory= new MailSSLSocketFactory();
socketFactory.setTrustAllHosts(true);
Properties props = System.getProperties();
props.put("mail.imaps.ssl.socketFactory", socketFactory);
//此处规定连接邮箱的方式是https还是http 默认一种即可
props.setProperty("mail.imap.socketFactory.class", mailinfo.getSslable() ? SSL_FACTORY : DEFAULT_FACTORY);
//对应邮箱服务器的端口 ssl对应465 普通对应25 视实际情况而定
props.setProperty("mail.imap.socketFactory.port", mailinfo.getMailboxPort().toString());
props.setProperty("mail.store.protocol", "imap");
//收信时邮箱服务器地址 比如qq邮箱对应imap.exmail.qq.com
props.setProperty("mail.imap.host", mailinfo.getMailbox());
//收信使用的端口 ssl对应993 普通对应143 视实际情况而定
props.setProperty("mail.imap.port", mailinfo.getMailboxPort().toString());
props.setProperty("mail.imap.auth.login.disable", "true");
props.setProperty("mail.imap.partialfetch", "false");
props.setProperty("mail.imaps.partialfetch", "false");
Session session = Session.getInstance(props, null);
//是否开启debug模式 如果设置成true 你将得到大量邮件log信息帮助调试
session.setDebug(false);
return session;
}
构建纯文本无附件的邮件:
public void SendJustText(Session session) throws MessagingException {
MimeMessage message = new MimeMessage(session);
//邮件标题
message.setSubject("纯文本");
//邮件发送人
message.setFrom(new InternetAddress(MailService.senderMail));
//邮件接收人 message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//构建最外层的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("alternative");
//构建邮件正文Body
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent("我是纯文本正文","TEXT/PLAIN; charset=GB2312");
//将body追加到Mutipart
mimeMultipart.addBodyPart(mimeBodyPart);
message.setContent(mimeMultipart);
//连接邮箱服务器
Transport transport = session.getTransport("smtp");
//可以看成自己的邮箱登录 用时自行替换参数
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//发送消息
transport.sendMessage(message, message.getAllRecipients());
}
这样我们就得到了一个纯文本的邮件
纯文本.png
构建纯文本带附件的邮件:
--- MimeMutiPart("MIXED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1 (""APPLICATION/OCTET-STREAM")
|------MimeBodyPart2 (""APPLICATION/OCTET-STREAM")
public void SendAttachText(Session session) throws MessagingException, IOException {
MimeMessage message = new MimeMessage(session);
//邮件标题
message.setSubject("纯文本带附件");
//邮件发送人
message.setFrom(new InternetAddress(MailService.senderMail));
//邮件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//构建最外层的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("mixed");
//构建邮件正文Body
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent("我是纯文本带附件正文","TEXT/PLAIN; charset=GB2312");
//将body追加到Mutipart
mimeMultipart.addBodyPart(mimeBodyPart);
//添加第一个附件
mimeMultipart.addBodyPart(getAttchment1());
//添加第二个附件
mimeMultipart.addBodyPart(getAttchment2());
message.setContent(mimeMultipart);
//连接邮箱服务器
Transport transport = session.getTransport("smtp");
//可以看成自己的邮箱登录
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//发送消息
transport.sendMessage(message, message.getAllRecipients());
}
/**
* 构建附件1
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment1() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\xxx\\Desktop\\spring初始化.txt");
attachPart.attachFile(file);
//解决中文乱码问题
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
return attachPart;
}
/**
* 构建附件2
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment2() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\xxx\\Desktop\\长春农商行问题修复\\操作说明.txt");
attachPart.attachFile(file);
//解决中文乱码问题
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
return attachPart;
}
这样我们就得到了一个纯文本带附件的邮件
纯文本带附件.png
这里我们会发现一个问题:
一般来讲邮件附件不是从本地获取 而是服务器接收客户端发过来的文件,这样如何写呢?于是我们的getAttchment1方法就变成了这样:
/**
* 构建附件1
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment1(MultipartFile attachFile) throws IOException, MessagingException {
if (attachFile != null) {
MimeBodyPart attachPart = new MimeBodyPart();
ByteArrayDataSource byteArrayDataSource = new ByteArrayDataSource(multipartFile.getInputStream(),multipartFile.getContentType());
//为文件流设置文件名称
byteArrayDataSource.setName(multipartFile.getOriginalFilename());
DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(multipartFile.getInputStream(),multipartFile.getContentType()));
attachPart.setDataHandler(new DataHandler(dataSource));
//这里文件源名称由客户端上传 一般都是经过url编码的 我们先解码 再转成邮箱的编码
attachPart.setFileName(MimeUtility.encodeText(URLDecoder.decode(attachFile.getOriginalFilename(), "utf-8")));
}
}
构建图文混合无附件的邮件:
--- MimeMutiPart("RELATED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1("IMAGE/GIF")
|------MimeBodyPart2("IMAGE/GIF")
|------MimeBodyPart3("IMAGE/GIF")
public void SendJianShuPicText(Session session) throws MessagingException {
MimeMessage message = new MimeMessage(session);
//邮件标题
message.setSubject("富文本无附件");
//邮件发送人
message.setFrom(new InternetAddress(MailService.senderMail));
//邮件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//构建最外层的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("related");
//将文本的bodyPart追加到Mutipart
mimeMultipart.addBodyPart(getAlterBodyPart());
message.setContent(mimeMultipart);
//连接邮箱服务器
Transport transport = session.getTransport("smtp");
//可以看成自己的邮箱登录
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//发送消息
transport.sendMessage(message, message.getAllRecipients());
}
public MimeBodyPart getAlterBodyPart() throws MessagingException {
//要返回的alterBodyPart
MimeBodyPart alterBodyPart = new MimeBodyPart();
//定义alterBodyPart下的mutiPart
MimeMultipart alterMutiPart = new MimeMultipart("ALTERNATIVE");
//定义文本bodyPart
MimeBodyPart textBodyPart = new MimeBodyPart();
textBodyPart.setContent("我是富文本无附件正文","TEXT/PLAIN; charset=GB2312");
//定义html的bodyPart
MimeBodyPart htmlBodyPart =new MimeBodyPart();
htmlBodyPart.setContent("<html><div><h3>我是富文本无附件正文</h3></div></br><img src='cid:lenglengliang1'/></br><strong>我的冷冷凉2</strong></br><img src='cid:lenglengliang2'/></br>不下地的冷冷凉</html>","TEXT/HTML; charset=GB2312");
//添加文本的bodyPart
alterMutiPart.addBodyPart(textBodyPart);
//添加html的bodyPart
alterMutiPart.addBodyPart(htmlBodyPart);
//定义inline附件的bodyPart
MimeBodyPart inlineBodyPart = new MimeBodyPart();
DataSource dataSource = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg.jpg"));
inlineBodyPart.setDataHandler(new DataHandler(dataSource));
inlineBodyPart.setDisposition(MimeBodyPart.INLINE);
inlineBodyPart.setContentID("<lenglengliang1>");
//添加inline的bodyPart
alterMutiPart.addBodyPart(inlineBodyPart);
MimeBodyPart inlineBodyPart2 = new MimeBodyPart();
DataSource dataSource2 = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg (2).jpg"));
inlineBodyPart2.setDataHandler(new DataHandler(dataSource2));
inlineBodyPart2.setDisposition(MimeBodyPart.INLINE);
inlineBodyPart2.setContentID("<lenglengliang2>");
//添加inline的bodyPart
alterMutiPart.addBodyPart(inlineBodyPart2);
//装载mutiPart
alterBodyPart.setContent(alterMutiPart);
return alterBodyPart;
}
这样我们就得到了一个富文本无附件的邮件
富文本无附件
将图(不管是图片还是一些表情)添加到正文的逻辑是:
1.首先保证html文本中包含img标签 富文本在邮箱中显示的其实就是一个html页面
2.图片和<img src="" />的关联关系就是以下代码创建的
htmlBodyPart.setContent("<html><img src='cid:lenglengliang1'/></html>","TEXT/HTML; charset=GB2312");
inlineBodyPart.setContentID("<lenglengliang1>");
html中的src 必须包含cid: 这是rfc标准格式
bodyPart.setContentID 并不包含"cid:",但却有 "<>" 标签 这是邮件的标准格式,如果不带,发送时可能没问题,但是这个邮件被安卓或者ios端转发解析时可能出现问题,他们的标准库判断了"<>"
PS:项目中笔者曾经为图文中的图发送后变成了附件而苦恼了很久,原因就是这个cid src中需要带但setContentID时却不需要带
构建图文混合带附件的邮件:
这是最复杂的也是最常用的
--- MimeMutiPart("MIXED")
|------MimeBodyPart0 ---- MimeMutiPart("RELATED")
| MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
| MimeBodyPart1("IMAGE/GIF")
| MimeBodyPart2("IMAGE/GIF")
| MimeBodyPart3("IMAGE/GIF")
|------MimeBodyPart1 ("APPLICATION/OCTET-STREAM")
|------MimeBodyPart2 ("APPLICATION/OCTET-STREAM")
|------MimeBodyPart3 ("APPLICATION/OCTET-STREAM")
public void SendJianShuAttachPicText(Session session) throws MessagingException, IOException {
MimeMessage message = new MimeMessage(session);
//邮件标题
message.setSubject("富文本带附件");
//邮件发送人
message.setFrom(new InternetAddress(MailService.senderMail));
//邮件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//构建最外层的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("related");
//将文本的bodyPart追加到Mutipart
mimeMultipart.addBodyPart(getAlterBodyPart());
message.setContent(mimeMultipart);
//连接邮箱服务器
Transport transport = session.getTransport("smtp");
//可以看成自己的邮箱登录
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//发送消息
transport.sendMessage(message, message.getAllRecipients());
}
public MimeBodyPart getAlterBodyPart() throws MessagingException, IOException {
//要返回的alterBodyPart
MimeBodyPart alterBodyPart = new MimeBodyPart();
//定义alterBodyPart下的mutiPart
MimeMultipart alterMutiPart = new MimeMultipart("ALTERNATIVE");
//定义文本bodyPart
MimeBodyPart textBodyPart = new MimeBodyPart();
textBodyPart.setContent("我是富文本带附件正文","TEXT/PLAIN; charset=GB2312");
//定义html的bodyPart
MimeBodyPart htmlBodyPart =new MimeBodyPart();
htmlBodyPart.setContent("<html><div><h3>我是富文本带附件正文</h3></div></br><img src='cid:lenglengliang1'/></br><strong>我的冷冷凉2</strong></br><img src='cid:lenglengliang2'/></br>不下地的冷冷凉</html>","TEXT/HTML; charset=GB2312");
//添加文本的bodyPart
alterMutiPart.addBodyPart(textBodyPart);
//添加html的bodyPart
alterMutiPart.addBodyPart(htmlBodyPart);
//定义inline附件的bodyPart
MimeBodyPart inlineBodyPart = new MimeBodyPart();
DataSource dataSource = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg.jpg"));
inlineBodyPart.setDataHandler(new DataHandler(dataSource));
inlineBodyPart.setDisposition(MimeBodyPart.INLINE);
inlineBodyPart.setContentID("<lenglengliang1>");
//添加inline的bodyPart
alterMutiPart.addBodyPart(inlineBodyPart);
MimeBodyPart inlineBodyPart2 = new MimeBodyPart();
DataSource dataSource2 = new FileDataSource(new File("D:\\一些图片\\无聊的图片\\未来战士\\timg (2).jpg"));
inlineBodyPart2.setDataHandler(new DataHandler(dataSource2));
inlineBodyPart2.setDisposition(MimeBodyPart.INLINE);
inlineBodyPart2.setContentID("<lenglengliang2>");
//添加inline的bodyPart
alterMutiPart.addBodyPart(inlineBodyPart2);
//添加第一个附件
alterMutiPart.addBodyPart(getAttchment1());
//添加第二个附件
alterMutiPart.addBodyPart(getAttchment2());
//添加第三个附件
alterMutiPart.addBodyPart(getAttchment3());
//装载mutiPart
alterBodyPart.setContent(alterMutiPart);
return alterBodyPart;
}
/**
* 构建附件1
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment1() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\yangzhi\\Desktop\\spring初始化.txt");
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
return attachPart;
}
/**
* 构建附件2
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment2() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\yangzhi\\Desktop\\转发富文本(ceomg).eml");
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
return attachPart;
}
/**
* 构建附件3
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment3() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\yangzhi\\Desktop\\邮箱开发准备\\邮箱id比对.txt");
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
return attachPart;
}
这样我们就得到了一个富文本带附件的邮件
富文本带附件.png
邮件的结构精美的地方就是MutiPart和BodyPart的相互转换,文本虽多,但很多点都可以封装,为了方便看笔者就直接上的流水账,邮件的4个基本结构是构建我们代码思路的前提,感谢观看,如果此文对您有帮助 请点个小红心支持一下,后续的收邮件以及邮件的回执、一些坑会单独拿出来分享