總體介紹:
公司近期接到需求,由于安卓和IOS客戶端在轉(zhuǎn)發(fā)郵件時(shí)會(huì)重新下載一遍老郵件里附件粥帚,這個(gè)過程比較耗時(shí),體驗(yàn)不佳限次,現(xiàn)在需要實(shí)現(xiàn)客戶端走服務(wù)器來轉(zhuǎn)發(fā)芒涡,免去下載的過程
我們先了解一下兩種協(xié)議格式:
SMTP:郵件發(fā)送協(xié)議 ssl對(duì)應(yīng)端口465 非ssl對(duì)應(yīng)端口25
IMAP:收郵件協(xié)議 ssl對(duì)應(yīng)端口993 非ssl對(duì)應(yīng)端口143
以上描述的是郵箱服務(wù)器的默認(rèn)端口 可自定義或變更 但主流端口就是以上這么幾個(gè)
附上JavaMail官網(wǎng)地址,遇到問題時(shí)在里面會(huì)發(fā)現(xiàn)很多靈感:
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郵件格式介紹
如果想了解郵件如何發(fā)送,我們必須了解郵件消息的正文結(jié)構(gòu)
我們先了解一下發(fā)郵件時(shí)的流程:
//創(chuàng)建消息對(duì)象
MimeMessage message = new MimeMessage(session);
//創(chuàng)建消息正文
MimeMultipart mimmultiPart = new MimeMultipart();
//創(chuàng)建消息體
MimeBodyPart mimeBodyPart = new MimeBodyPart();
//將消息體包裹在正文中
mimmultiPart.addBodyPart(mimeBodyPart);
//將正文包裹在消息對(duì)象中
message.setContent(mimmultiPart )
//發(fā)郵件協(xié)議是smtp 收郵件協(xié)議是imap
Transport transport = session.getTransport("smtp");
//發(fā)送郵件
transport.sendMessage(mimeMessage,"某收件人");
MimeMessage 包含了 MimeMultipart,
MimeMultipart 包含了 MimeBodyPart
這里面比較絕妙的就是
MimeBodyPart 其實(shí)反過來也可以包含MimeMultipart 后面我們將看到
以下4種格式均為筆者通過FoxMail發(fā)送郵件费尽,并通過JavaMail去找郵件而來的結(jié)果
①純文本
郵件正文僅帶有文本文字
--- MimeMutiPart("ALTERNATIVE")
MimeBodyPart0("TEXT/PLAIN")
MimeBodyPart1("TEXT/HTML")
使用FoxMail發(fā)郵件時(shí)赠群,盡管是最簡單的文本,也是一個(gè)MimeMutiPart包含了兩個(gè)MimeBodyPart依啰,一個(gè)對(duì)應(yīng)純文本乎串,另一個(gè)對(duì)應(yīng)html
②純文本帶附件
郵件正文包含文本文字+附件
--- MimeMutiPart("MIXED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1 (""APPLICATION/OCTET-STREAM")
文本的內(nèi)容仍然是一個(gè)MutiPart包含了兩個(gè)BodyPart 只不過此部分被包含進(jìn)了MimeBodyPart0
附件的部分是一個(gè)獨(dú)立的MimeBodyPart1
③富文本
郵件正文包含富文本文字
--- MimeMutiPart("RELATED")
|------MimeBodyPart0 ---- MimeMutiPart("ALTERNATIVE")
| MimeBodyPart0("TEXT/PLAIN")
| MimeBodyPart1("TEXT/HTML")
|------MimeBodyPart1("IMAGE/GIF")
|------MimeBodyPart2("IMAGE/GIF")
|------MimeBodyPart3("IMAGE/GIF")
文本的內(nèi)容還是相同的,一個(gè)MutiPart包含了兩個(gè)BodyPart 此MutiPart被包含進(jìn)了
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")
這是最復(fù)雜的一種鸯两,也是可以基本滿足我們?nèi)粘P枨蟮囊环N闷旧,最里面的一層還是ALTERNATIVE的MutiPart包含了兩個(gè)文本類型,同層級(jí)包含著"INLINE"的附件
最外層是一些真正的附件
以上四種類型基本上涵蓋了我們?nèi)粘J褂玫母袷骄疲浑y發(fā)現(xiàn)MimeMutiPart一共有三種類型:
1.MIXED:包含附件的文檔
2.ALTERNATIVE:
包含text文檔和html文檔忙灼,為什么是兩種 一般郵箱客戶端比如FoxMail 可以切換閱讀模式
一個(gè)是超文本閱讀格式 對(duì)應(yīng)html
一個(gè)是純文本(去掉文中圖片和表情)格式 對(duì)應(yīng)text
3.RELATED:圖文(富文本)
發(fā)送郵件代碼樣例
首先,郵箱服務(wù)都是基于Session會(huì)話開始钝侠,首先我們構(gòu)建一個(gè)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);
//此處規(guī)定連接郵箱的方式是https還是http 默認(rèn)一種即可
props.setProperty("mail.imap.socketFactory.class", mailinfo.getSslable() ? SSL_FACTORY : DEFAULT_FACTORY);
//對(duì)應(yīng)郵箱服務(wù)器的端口 ssl對(duì)應(yīng)465 普通對(duì)應(yīng)25 視實(shí)際情況而定
props.setProperty("mail.imap.socketFactory.port", mailinfo.getMailboxPort().toString());
props.setProperty("mail.store.protocol", "imap");
//收信時(shí)郵箱服務(wù)器地址 比如qq郵箱對(duì)應(yīng)imap.exmail.qq.com
props.setProperty("mail.imap.host", mailinfo.getMailbox());
//收信使用的端口 ssl對(duì)應(yīng)993 普通對(duì)應(yīng)143 視實(shí)際情況而定
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模式 如果設(shè)置成true 你將得到大量郵件log信息幫助調(diào)試
session.setDebug(false);
return session;
}
構(gòu)建純文本無附件的郵件:
public void SendJustText(Session session) throws MessagingException {
MimeMessage message = new MimeMessage(session);
//郵件標(biāo)題
message.setSubject("純文本");
//郵件發(fā)送人
message.setFrom(new InternetAddress(MailService.senderMail));
//郵件接收人 message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//構(gòu)建最外層的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("alternative");
//構(gòu)建郵件正文Body
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent("我是純文本正文","TEXT/PLAIN; charset=GB2312");
//將body追加到Mutipart
mimeMultipart.addBodyPart(mimeBodyPart);
message.setContent(mimeMultipart);
//連接郵箱服務(wù)器
Transport transport = session.getTransport("smtp");
//可以看成自己的郵箱登錄 用時(shí)自行替換參數(shù)
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//發(fā)送消息
transport.sendMessage(message, message.getAllRecipients());
}
這樣我們就得到了一個(gè)純文本的郵件
構(gòu)建純文本帶附件的郵件:
--- 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);
//郵件標(biāo)題
message.setSubject("純文本帶附件");
//郵件發(fā)送人
message.setFrom(new InternetAddress(MailService.senderMail));
//郵件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//構(gòu)建最外層的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("mixed");
//構(gòu)建郵件正文Body
MimeBodyPart mimeBodyPart = new MimeBodyPart();
mimeBodyPart.setContent("我是純文本帶附件正文","TEXT/PLAIN; charset=GB2312");
//將body追加到Mutipart
mimeMultipart.addBodyPart(mimeBodyPart);
//添加第一個(gè)附件
mimeMultipart.addBodyPart(getAttchment1());
//添加第二個(gè)附件
mimeMultipart.addBodyPart(getAttchment2());
message.setContent(mimeMultipart);
//連接郵箱服務(wù)器
Transport transport = session.getTransport("smtp");
//可以看成自己的郵箱登錄
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//發(fā)送消息
transport.sendMessage(message, message.getAllRecipients());
}
/**
* 構(gòu)建附件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;
}
/**
* 構(gòu)建附件2
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment2() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\xxx\\Desktop\\長春農(nóng)商行問題修復(fù)\\操作說明.txt");
attachPart.attachFile(file);
//解決中文亂碼問題
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
return attachPart;
}
這樣我們就得到了一個(gè)純文本帶附件的郵件
這里我們會(huì)發(fā)現(xiàn)一個(gè)問題:
一般來講郵件附件不是從本地獲取 而是服務(wù)器接收客戶端發(fā)過來的文件该园,這樣如何寫呢?于是我們的getAttchment1方法就變成了這樣:
/**
* 構(gòu)建附件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());
//為文件流設(shè)置文件名稱
byteArrayDataSource.setName(multipartFile.getOriginalFilename());
DataHandler dataHandler = new DataHandler(new ByteArrayDataSource(multipartFile.getInputStream(),multipartFile.getContentType()));
attachPart.setDataHandler(new DataHandler(dataSource));
//這里文件源名稱由客戶端上傳 一般都是經(jīng)過url編碼的 我們先解碼 再轉(zhuǎn)成郵箱的編碼
attachPart.setFileName(MimeUtility.encodeText(URLDecoder.decode(attachFile.getOriginalFilename(), "utf-8")));
}
}
構(gòu)建圖文混合無附件的郵件:
--- 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);
//郵件標(biāo)題
message.setSubject("富文本無附件");
//郵件發(fā)送人
message.setFrom(new InternetAddress(MailService.senderMail));
//郵件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//構(gòu)建最外層的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("related");
//將文本的bodyPart追加到Mutipart
mimeMultipart.addBodyPart(getAlterBodyPart());
message.setContent(mimeMultipart);
//連接郵箱服務(wù)器
Transport transport = session.getTransport("smtp");
//可以看成自己的郵箱登錄
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//發(fā)送消息
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:\\一些圖片\\無聊的圖片\\未來戰(zhàn)士\\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:\\一些圖片\\無聊的圖片\\未來戰(zhàn)士\\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;
}
這樣我們就得到了一個(gè)富文本無附件的郵件
將圖(不管是圖片還是一些表情)添加到正文的邏輯是:
1.首先保證html文本中包含img標(biāo)簽 富文本在郵箱中顯示的其實(shí)就是一個(gè)html頁面
2.圖片和<img src="" />的關(guān)聯(lián)關(guān)系就是以下代碼創(chuàng)建的
htmlBodyPart.setContent("<html><img src='cid:lenglengliang1'/></html>","TEXT/HTML; charset=GB2312");
inlineBodyPart.setContentID("<lenglengliang1>");
html中的src 必須包含cid: 這是rfc標(biāo)準(zhǔn)格式
bodyPart.setContentID 并不包含"cid:",但卻有 "<>" 標(biāo)簽 這是郵件的標(biāo)準(zhǔn)格式,如果不帶帅韧,發(fā)送時(shí)可能沒問題里初,但是這個(gè)郵件被安卓或者ios端轉(zhuǎn)發(fā)解析時(shí)可能出現(xiàn)問題,他們的標(biāo)準(zhǔn)庫判斷了"<>"
PS:項(xiàng)目中筆者曾經(jīng)為圖文中的圖發(fā)送后變成了附件而苦惱了很久,原因就是這個(gè)cid src中需要帶但setContentID時(shí)卻不需要帶
構(gòu)建圖文混合帶附件的郵件:
這是最復(fù)雜的也是最常用的
--- 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);
//郵件標(biāo)題
message.setSubject("富文本帶附件");
//郵件發(fā)送人
message.setFrom(new InternetAddress(MailService.senderMail));
//郵件接收人
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse(MailService.senderMail));
//構(gòu)建最外層的MimeMutipart
MimeMultipart mimeMultipart = new MimeMultipart("related");
//將文本的bodyPart追加到Mutipart
mimeMultipart.addBodyPart(getAlterBodyPart());
message.setContent(mimeMultipart);
//連接郵箱服務(wù)器
Transport transport = session.getTransport("smtp");
//可以看成自己的郵箱登錄
transport.connect(MailService.mailHost, MailService.senderMail, MailService.password);
//發(fā)送消息
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:\\一些圖片\\無聊的圖片\\未來戰(zhàn)士\\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:\\一些圖片\\無聊的圖片\\未來戰(zhàn)士\\timg (2).jpg"));
inlineBodyPart2.setDataHandler(new DataHandler(dataSource2));
inlineBodyPart2.setDisposition(MimeBodyPart.INLINE);
inlineBodyPart2.setContentID("<lenglengliang2>");
//添加inline的bodyPart
alterMutiPart.addBodyPart(inlineBodyPart2);
//添加第一個(gè)附件
alterMutiPart.addBodyPart(getAttchment1());
//添加第二個(gè)附件
alterMutiPart.addBodyPart(getAttchment2());
//添加第三個(gè)附件
alterMutiPart.addBodyPart(getAttchment3());
//裝載mutiPart
alterBodyPart.setContent(alterMutiPart);
return alterBodyPart;
}
/**
* 構(gòu)建附件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;
}
/**
* 構(gòu)建附件2
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment2() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\yangzhi\\Desktop\\轉(zhuǎn)發(fā)富文本(ceomg).eml");
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
return attachPart;
}
/**
* 構(gòu)建附件3
* @return
* @throws IOException
* @throws MessagingException
*/
private MimeBodyPart getAttchment3() throws IOException, MessagingException {
MimeBodyPart attachPart = new MimeBodyPart();
File file = new File("C:\\Users\\yangzhi\\Desktop\\郵箱開發(fā)準(zhǔn)備\\郵箱id比對(duì).txt");
attachPart.attachFile(file);
attachPart.setFileName(MimeUtility.encodeText(file.getName()));
attachPart.setDisposition(MimeBodyPart.ATTACHMENT);
return attachPart;
}
這樣我們就得到了一個(gè)富文本帶附件的郵件
郵件的結(jié)構(gòu)精美的地方就是MutiPart和BodyPart的相互轉(zhuǎn)換忽舟,文本雖多双妨,但很多點(diǎn)都可以封裝,為了方便看筆者就直接上的流水賬叮阅,郵件的4個(gè)基本結(jié)構(gòu)是構(gòu)建我們代碼思路的前提刁品,感謝觀看,如果此文對(duì)您有幫助 請(qǐng)點(diǎn)個(gè)小紅心支持一下浩姥,后續(xù)的收郵件以及郵件的回執(zhí)挑随、一些坑會(huì)單獨(dú)拿出來分享