JavaMail API是讀取、撰寫、發送電子信息的可選包。咱們可用它來創建如Eudora、Foxmail、MS Outlook Express通常的郵件用戶代理程序(Mail User Agent,簡稱MUA)。讓咱們看看JavaMail API是如何提供信息訪問功能的吧!JavaMail API被設計用於以不依賴協議的方式去發送和接收電子信息,文中着重:如何以不依賴於協議的方式發送接收電子信息,這也是本文所要描述的.html
版權聲明:本文能夠自由轉載,轉載時請務必以超連接形式標明文章原始出處和做者信息及本聲明java
做者:cleverpig(做者的Blog:http://blog.matrix.org.cn/page/cleverpig)數組
原文:http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html安全
關鍵字:java,mail,pop,smtp服務器
1、JavaMail API簡介session
JavaMail API是讀取、撰寫、發送電子信息的可選包。咱們可用它來創建如Eudora、Foxmail、MS Outlook Express通常的郵件用戶代理程序(Mail User Agent,簡稱MUA)。而不是像sendmail或者其它的郵件傳輸代理(Mail Transfer Agent,簡稱MTA)程序那樣能夠傳送、遞送、轉發郵件。從另一個角度來看,咱們這些電子郵件用戶平常用MUA程序來讀寫郵件,而MUA依賴着MTA處理郵件的遞送。併發
在清楚了到MUA與MTA之間的關係後,讓咱們看看JavaMail API是如何提供信息訪問功能的吧!JavaMail API被設計用於以不依賴協議的方式去發送和接收電子信息,這個API被分爲兩大部分:框架
基本功能:如何以不依賴於協議的方式發送接收電子信息,這也是本文所要描述的,不過在下文中,你們將看到這只是一廂情願而已。ide
第二個部分則是依賴特定協議的,好比SMTP、POP、IMAP、NNTP協議。在這部分的JavaMail API是爲了和服務器通信,並不在本文的內容中。學習
2、相關協議一覽
在咱們步入JavaMail API以前,先看一下API所涉及的協議。如下即是你們平常所知、所樂於使用的4大信息傳輸協議:
SMTP
POP
IMAP
MIME
固然,上面的4個協議,並非所有,還有NNTP和其它一些協議可用於傳輸信息,可是因爲不經常使用到,因此本文便不說起了。理解這4個基本的協議有助於咱們更好的使用JavaMail API。然而JavaMail API是被設計爲與協議無關的,目前咱們並不能克服這些協議的束縛。確切的說,若是咱們使用的功能並不被咱們選擇的協議支持,那麼JavaMail API並不可能如魔術師同樣神奇的賦予咱們這種能力。
1.SMTP
簡單郵件傳輸協議定義了遞送郵件的機制。在下文中,咱們將使用基於Java-Mail的程序與公司或者ISP的SMTP服務器進行通信。這個SMTP服務器將郵件轉發到接收者的SMTP服務器,直至最後被接收者經過POP或者IMAP協議獲取。這並不須要SMTP服務器使用支持受權的郵件轉發,可是卻的確要注意SMTP服務器的正確設置(SMTP服務器的設置與JavaMail API無關)。
2.POP
POP是一種郵局協議,目前爲第3個版本,即衆所周知的POP3。POP定義了一種用戶如何得到郵件的機制。它規定了每一個用戶使用一個單獨的郵箱。大多數人在使用POP時所熟悉的功能並不是都被支持,例如查看郵箱中的新郵件數量。而這個功能是微軟的Outlook內建的,那麼就說明微軟Outlook之類的郵件客戶端軟件是經過查詢最近收到的郵件來計算新郵件的數量來實現前面所說的功能。所以在咱們使用JavaMail API時須要注意,當須要得到如前面所講的新郵件數量之類的信息時,咱們不得不本身進行計算。
3.IMAP
IMAP使用在接收信息的高級協議,目前版本爲第4版,因此也被稱爲IMAP4。須要注意的是在使用IMAP時,郵件服務器必須支持該協議。從這個方面講,咱們並不能徹底使用IMAP來替代POP,不能期待IMAP在任何地方都被支持。假如郵件服務器支持IMAP,那麼咱們的郵件程序將可以具備如下被IMAP所支持的特性:每一個用戶在服務器上可具備多個目錄,這些目錄能在多個用戶之間共享。
其與POP相比高級之處顯而易見,可是在嘗試採起IMAP時,咱們認識到它並非十分完美的:因爲IMAP須要從其它服務器上接收新信息,將這些信息遞送給用戶,維護每一個用戶的多個目錄,這都爲郵件服務器帶來了高負載。而且IMAP與POP的一個不一樣之處是POP用戶在接收郵件時將從郵件服務器上下載郵件,而IMAP容許用戶直接訪問郵件目錄,因此在郵件服務器進行備份做業時,因爲每一個長期使用此郵件系統的用戶所用的郵件目錄會佔有很大的空間,這將直接致使郵件服務器上磁盤空間暴漲。
4.MIME
MIME並非用於傳送郵件的協議,它做爲多用途郵件的擴展定義了郵件內容的格式:信息格式、附件格式等等。一些RFC標準都涉及了MIME:RFC 822, RFC 2045, RFC 2046, RFC 2047,有興趣的Matrixer能夠閱讀一下。而做爲JavaMail API的開發者,咱們並不需關心這些格式定義,可是這些格式被用在了程序中。
5.NNTP和其它的第三方協議
正由於JavaMail API在設計時考慮到與第三方協議實現提供商之間的分離,故咱們能夠很容易的添加一些第三方協議。SUN維護着一個第三方協議實現提供商的列表:http://java.sun.com/products/javamail/Third_Party.html,經過此列表咱們能夠找到所須要的而又不被SUN提供支持的第三方協議:好比NNTP這個新聞組協議和S/MIME這個安全的MIME協議。
3、安裝
1.安裝JavaMail
爲了使用JavaMail API,須要從http://java.sun.com/products/javamail/downloads/index.html下載文件名格式爲javamail-[version].zip的文件(這個文件中包括了JavaMail實現),並將其中的mail.jar文件添加到CLASSPATH中。這個實現提供了對SMTP、IMAP四、POP3的支持。
注意:在安裝JavaMail實現以後,咱們將在demo目錄中發現許多有趣的簡單實例程序。
在安裝了JavaMail以後,咱們還須要安裝JavaBeans Activation Framework,由於這個框架是JavaMail API所須要的。若是咱們使用J2EE的話,那麼咱們並沒有需單獨下載JavaMail,由於它存在於J2EE.jar中,只需將J2EE.jar加入到CLASSPATH便可。
2.安裝JavaBeans Activation Framework
從http://java.sun.com/products/javabeans/glasgow/jaf.html下載JavaBeans Activation Framework,並將其添加到CLASSPATH中。此框架增長了對任何數據塊的分類、以及對它們的處理的特性。這些特性是JavaMail API須要的。雖然聽起來這些特性很是模糊,可是它對於咱們的JavaMail API來講只是提供了基本的MIME類型支持。
到此爲止,咱們應當把mail.jar和activation.jar都添加到了CLASSPATH中。
固然若是從方便的角度講,直接把這兩個Jar文件複製到JRE目錄的lib/ext目錄中也能夠。
4、初次認識JavaMail API
1.瞭解咱們的JavaMail環境
A.縱覽JavaMail核心類結構
打開JavaMail.jar文件,咱們將發如今javax.mail的包下面存在着一些核心類:Session、Message、Address、Authenticator、Transport、Store、Folder。並且在javax.mail.internet包中還有一些經常使用的子類。
B.Session
Session類定義了基本的郵件會話。就像Http會話那樣,咱們進行收發郵件的工做都是基於這個會話的。Session對象利用了java.util.Properties對象得到了郵件服務器、用戶名、密碼信息和整個應用程序都要使用到的共享信息。
Session類的構造方法是私有的,因此咱們可使用Session類提供的getDefaultInstance()這個靜態工廠方法得到一個默認的Session對象:
Properties props = new Properties();
// fill props with any information
Session session = Session.getDefaultInstance(props, null);
或者使用getInstance()這個靜態工廠方法得到自定義的Session:
Properties props = new Properties();
// fill props with any information
Session session = Session.getInstance(props, null);
從上面的兩個例子中不難發現,getDefaultInstance()和getInstance()方法的第二個參數都是null,這是由於在上面的例子中並無使用到郵件受權,下文中將對受權進行詳細介紹。
從不少的實例看,在對mail server進行訪問的過程當中使用共享的Session是足夠的,即便是工做在多個用戶郵箱的模式下也不例外。
C.Message
當咱們創建了Session對象後,即可以被髮送的構造信息體了。在這裏SUN提供了Message類型來幫助開發者完成這項工做。因爲Message是一個抽象類,大多數狀況下,咱們使用javax.mail.internet.MimeMessage這個子類,該類是使用MIME類型、MIME信息頭的郵箱信息。信息頭只能使用US-ASCII字符,而非ASCII字符將經過編碼轉換爲ASCII的方式使用。
爲了創建一個MimeMessage對象,咱們必須將Session對象做爲MimeMessage構造方法的參數傳入:
MimeMessage message = new MimeMessage(session);
注意:對於MimeMessage類來說存在着多種構造方法,好比使用輸入流做爲參數的構造方法。
在創建了MimeMessage對象後,咱們須要設置它的各個part,對於MimeMessage類來講,這些part就是MimePart接口。最基本的設置信息內容的方法就是經過表示信息內容和米麼類型的參數調用setContent()方法:
message.setContent("Hello", "text/plain");
然而,若是咱們所使用的MimeMessage中信息內容是文本的話,咱們即可以直接使用setText()方法來方便的設置文本內容。
message.setText("Hello");
前面所講的兩種方法,對於文本信息,後者更爲合適。而對於其它的一些信息類型,好比HTML信息,則要使用前者。
別忘記了,使用setSubject()方法對郵件設置郵件主題:
message.setSubject("First");
D.Address
到這裏,咱們已經創建了Session和Message,下面將介紹如何使用郵件地址類:Address。像Message同樣,Address類也是一個抽象類,因此咱們將使用javax.mail.internet.InternetAddress這個子類。
經過傳入表明郵件地址的字符串,咱們能夠創建一個郵件地址類:
Address address = new InternetAddress("president@whitehouse.gov");
若是要在郵件地址後面增長名字的話,能夠經過傳遞兩個參數:表明郵件地址和名字的字符串來創建一個具備郵件地址和名字的郵件地址類:
Address address = new InternetAddress("president@whitehouse.gov", "George Bush");
本文在這裏所講的郵件地址類是爲了設置郵件信息的發信人和收信人而準備的,在創建了郵件地址類後,咱們經過message的setFrom()和setReplyTo()兩種方法設置郵件的發信人:
message.setFrom(address);
message.setReplyTo(address);
若在郵件中存在多個發信人地址,咱們可用addForm()方法增長髮信人:
Address address[] = ...;
message.addFrom(address);
爲了設置收信人,咱們使用addRecipient()方法增長收信人,此方法須要使用Message.RecipientType的常量來區分收信人的類型:
message.addRecipient(type, address)
下面是Message.RecipientType的三個常量:
Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC
所以,若是咱們要發送郵件給總統,併發用一個副本給第一夫人的話,下面的方法將被用到:
Address toAddress = new InternetAddress("vice.president@whitehouse.gov");
Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");
message.addRecipient(Message.RecipientType.TO, toAddress);
message.addRecipient(Message.RecipientType.CC, ccAddress);
JavaMail API並無提供檢查郵件地址有效性的機制。固然咱們能夠本身完成這個功能:驗證郵件地址的字符是否按照RFC822規定的格式書寫或者經過DNS服務器上的MX記錄驗證等。
E.Authenticator
像java.net類那樣,JavaMail API經過使用受權者類(Authenticator)以用戶名、密碼的方式訪問那些受到保護的資源,在這裏「資源」就是指郵件服務器。在javax.mail包中能夠找到這個JavaMail的受權者類(Authenticator)。
在使用Authenticator這個抽象類時,咱們必須採用繼承該抽象類的方式,而且該繼承類必須具備返回PasswordAuthentication對象(用於存儲認證時要用到的用戶名、密碼)getPasswordAuthentication()方法。而且要在Session中進行註冊,使Session可以瞭解在認證時該使用哪一個類。
下面代碼片段中的MyAuthenticator就是一個Authenticator的子類。
Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
F.Transport
在發送信息時,Transport類將被用到。這個類實現了發送信息的協議(通稱爲SMTP),此類是一個抽象類,咱們可使用這個類的靜態方法send()來發送消息:
Transport.send(message);
固然,方法是多樣的。咱們也可由Session得到相應協議對應的Transport實例。並經過傳遞用戶名、密碼、郵件服務器主機名等參數創建與郵件服務器的鏈接,並使用sendMessage()方法將信息發送,最後關閉鏈接:
message.saveChanges(); // implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
評論:上面的方法是一個很好的方法,尤爲是在咱們在同一個郵件服務器上發送多個郵件時。由於這時咱們將在鏈接郵件服務器後連續發送郵件,而後再關閉掉鏈接。send()這個基本的方法是在每次調用時進行與郵件服務器的鏈接的,對於在同一個郵件服務器上發送多個郵件來說可謂低效的方式。
注意:若是須要在發送郵件過程當中監控mail命令的話,能夠在發送前設置debug標誌:
session.setDebug(true)。
G.Store和Folder
接收郵件和發送郵件很相似都要用到Session。可是在得到Session後,咱們須要從Session中獲取特定類型的Store,而後鏈接到Store,這裏的Store表明了存儲郵件的郵件服務器。在鏈接Store的過程當中,極有可能須要用到用戶名、密碼或者Authenticator。
// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);
在鏈接到Store後,一個Folder對象即目錄對象將經過Store的getFolder()方法被返回,咱們可從這個Folder中讀取郵件信息:
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
上面的例子首先從Store中得到INBOX這個Folder(對於POP3協議只有一個名爲INBOX的Folder有效),而後以只讀(Folder.READ_ONLY)的方式打開Folder,最後調用Folder的getMessages()方法獲得目錄中全部Message的數組。
注意:對於POP3協議只有一個名爲INBOX的Folder有效,而對於IMAP協議,咱們能夠訪問多個Folder(想一想前面講的IMAP協議)。並且SUN在設計Folder的getMessages()方法時採起了很智能的方式:首先接收新郵件列表,而後再須要的時候(好比讀取郵件內容)才從郵件服務器讀取郵件內容。
在讀取郵件時,咱們能夠用Message類的getContent()方法接收郵件或是writeTo()方法將郵件保存,getContent()方法只接收郵件內容(不包含郵件頭),而writeTo()方法將包括郵件頭。
System.out.println(((MimeMessage)message).getContent());
在讀取郵件內容後,別忘記了關閉Folder和Store。
folder.close(aBoolean);
store.close();
傳遞給Folder.close()方法的boolean 類型參數表示是否在刪除操做郵件後更新Folder。
H.繼續向前進!
在講解了以上的七個Java Mail核心類定義和理解了簡單的代碼片段後,下文將詳細講解怎樣使用這些類實現JavaMail API所要完成的高級功能。
5、使用JavaMail API
在明確了JavaMail API的核心部分如何工做後,本人將帶領你們學習一些使用Java Mail API任務案例。
1.發送郵件
在得到了Session後,創建並填入郵件信息,而後發送它到郵件服務器。這即是使用Java Mail API發送郵件的過程,在發送郵件以前,咱們須要設置SMTP服務器:經過設置Properties的mail.smtp.host屬性。
String host = ...;
String from = ...;
String to = ...;
// Get system properties
Properties props = System.getProperties();
// Setup mail server
props.put("mail.smtp.host", host);
// Get session
Session session = Session.getDefaultInstance(props, null);
// Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail");
message.setText("Welcome to JavaMail");
// Send message
Transport.send(message);
因爲創建郵件信息和發送郵件的過程當中可能會拋出異常,因此咱們須要將上面的代碼放入到try-catch結構塊中。
2.接收郵件
爲了在讀取郵件,咱們得到了session,而且鏈接到了郵箱的相應store,打開相應的Folder,而後獲得咱們想要的郵件,固然別忘記了在結束時關閉鏈接。
String host = ...;
String username = ...;
String password = ...;
// Create empty properties
Properties props = new Properties();
// Get session
Session session = Session.getDefaultInstance(props, null);
// Get the store
Store store = session.getStore("pop3");
store.connect(host, username, password);
// Get folder
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
}
// Close connection
folder.close(false);
store.close();
上面的代碼所做的是從郵箱中讀取每一個郵件,而且顯示郵件的發信人地址和主題。從技術角度講,這裏存在着一個異常的可能:當發信人地址爲空時,getFrom()[0]將拋出異常。
下面的代碼片段有效的說明了如何讀取郵件內容,在顯示每一個郵件發信人和主題後,將出現用戶提示從而獲得用戶是否讀取該郵件的確認,若是輸入YES的話,咱們可用Message.writeTo(java.io.OutputStream os)方法將郵件內容輸出到控制檯上,關於Message.writeTo()的具體用法請看JavaMail API。
BufferedReader reader = new BufferedReader (
new InputStreamReader(System.in));
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i
System.out.println(i + ": " + message[i].getFrom()[0]
+ "\t" + message[i].getSubject());
System.out.println("Do you want to read message? " +
"[YES to read/QUIT to end]");
String line = reader.readLine();
if ("YES".equals(line)) {
message[i].writeTo(System.out);
} else if ("QUIT".equals(line)) {
break;
}
}
3.刪除郵件和標誌
設置與message相關的Flags是刪除郵件的經常使用方法。這些Flags表示了一些系統定義和用戶定義的不一樣狀態。在Flags類的內部類Flag中預約義了一些標誌:
Flags.Flag.ANSWERED
Flags.Flag.DELETED
Flags.Flag.DRAFT
Flags.Flag.FLAGGED
Flags.Flag.RECENT
Flags.Flag.SEEN
Flags.Flag.USER
但須要在使用時注意的:標誌存在並不是意味着這個標誌被全部的郵件服務器所支持。例如,對於刪除郵件的操做,POP協議不支持上面的任何一個。因此要肯定哪些標誌是被支持的——經過訪問一個已經打開的Folder對象的getPermanetFlags()方法,它將返回當前被支持的Flags類對象。
刪除郵件時,咱們能夠設置郵件的DELETED標誌:
message.setFlag(Flags.Flag.DELETED, true);
可是首先要採用READ_WRITE的方式打開Folder:
folder.open(Folder.READ_WRITE);
在對郵件進行刪除操做後關閉Folder時,須要傳遞一個true做爲對刪除郵件的擦除確認。
folder.close(true);
Folder類中另外一種用於刪除郵件的方法expunge()也一樣可刪除郵件,可是它並不爲sun提供的POP3實現支持,而其它第三方提供的POP3實現支持或者並不支持這種方法。
另外,介紹一種檢查某個標誌是否被設置的方法:Message.isSet(Flags.Flag flag)方法,其中參數爲被檢查的標誌。
4.郵件認證
咱們在前面已經學會了如何使用Authenticator類來代替直接使用用戶名和密碼這兩字符串做爲Session.getDefaultInstance()或者Session.getInstance()方法的參數。在前面的小試牛刀後,如今咱們將瞭解到全面認識一下郵件認證。
咱們在此取代了直接使用郵件服務器主機名、用戶名、密碼這三個字符串做爲鏈接到POP3 Store的方式,使用存儲了郵件服務器主機名信息的屬性文件,並在得到Session時傳入自定義的Authenticator實例:
// Setup properties
Properties props = System.getProperties();
props.put("mail.pop3.host", host);
// Setup authentication, get session
Authenticator auth = new PopupAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
// Get the store
Store store = session.getStore("pop3");
store.connect();
PopupAuthenticator類繼承了抽象類Authenticator,而且經過重載Authenticator類的getPasswordAuthentication()方法返回PasswordAuthentication類對象。而getPasswordAuthentication()方法的參數param是以逗號分割的用戶名、密碼組成的字符串。
import javax.mail.*;
import java.util.*;
public class PopupAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication(String param) {
String username, password;
StringTokenizer st = new StringTokenizer(param, ",");
username = st.nextToken();
password = st.nextToken();
return new PasswordAuthentication(username, password);
}
}
5.回覆郵件
回覆郵件的方法很簡單:使用Message類的reply()方法,經過配置回覆郵件的收件人地址和主題(若是沒有提供主題的話,系統將默認將「Re:」做爲郵件的主體),這裏不須要設置任何的郵件內容,只要複製發信人或者reply-to到新的收件人。而reply()方法中的boolean參數表示是否將郵件回覆給發送者(參數值爲false),或是恢復給全部人(參數值爲true)。
補充一下,reply-to地址須要在發信時使用setReplyTo()方法設置。
MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);
6.轉發郵件
轉發郵件的過程不如前面的回覆郵件那樣簡單,它將創建一個轉發郵件,這並不是一個方法就能作到。
每一個郵件是由多個部分組成,每一個部分稱爲一個郵件體部分,是一個BodyPart類對象,對於MIME類型郵件來說就是MimeBodyPart類對象。這些郵件體包含在成爲Multipart的容器中對於MIME類型郵件來說就是MimeMultiPart類對象。在轉發郵件時,咱們創建一個文字郵件體部分和一個被轉發的文字郵件體部分,而後將這兩個郵件體放到一個Multipart中。說明一下,複製一個郵件內容到另外一個郵件的方法是僅複製它的DataHandler(數據處理者)便可。這是由JavaBeans Activation Framework定義的一個類,它提供了對郵件內容的操做命令的訪問、管理了郵件內容操做,是不一樣的數據源和數據格式之間的一致性接口。
// Create the message to forward
Message forward = new MimeMessage(session);
// Fill in header
forward.setSubject("Fwd: " + message.getSubject());
forward.setFrom(new InternetAddress(from));
forward.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(
"Here you go with the original message:\n\n");
// Create a multi-part to combine the parts
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Create and fill part for the forwarded content
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());
// Add part to multi part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
forward.setContent(multipart);
// Send message
Tran