1、JavaMail概述:html
JavaMail是由Sun定義的一套收發電子郵件的API,不一樣的廠商能夠提供本身的實現類。但它並無包含在JDK中,而是做爲JavaEE的一部分。java
廠商所提供的JavaMail服務程序能夠有選擇地實現某些郵件協議,常見的郵件協議包括:spring
l SMTP:簡單郵件傳輸協議,用於發送電子郵件的傳輸協議;編程
l POP3:用於接收電子郵件的標準協議;安全
l IMAP:互聯網消息協議,是POP3的替代協議。服務器
這三種協議都有對應SSL加密傳輸的協議,分別是SMTPS,POP3S和IMAPS。網絡
除JavaMail服務提供程序以外,JavaMail還須要JAF(JavaBeans Activation Framework)來處理不是純文本的郵件內容,這包括MIME(多用途互聯網郵件擴展)、URL頁面和文件附件等內容。下圖描述了JavaMail的體系結構。session
mail.jar:此JAR文件包含JavaMail API和Sun提供的SMTP、IMAP和POP3服務提供程序;併發
activation.jar:此JAR文件包含JAF API和Sun的實現。socket
2、對相關協議的回顧:
1、介紹
在研究 JavaMail API 的細則以前,讓咱們回顧用於 API 的協議。基本上,您會逐漸熟悉並喜好的協議有四個:
* SMTP
* POP
* IMAP
* MIME
您還將碰到 NNTP 和其它協議。理解全部協議的基本知識將有助於您理解如何使用 JavaMail API。雖然不瞭解這些協議您照樣能夠用這個 API,卻不可以克服那些基礎協議的侷限性。若是咱們精選的協議不能支持某種性能,JavaMail API 決不能魔術般的將這種性能添加上去。(您很快就會看到,在處理 POP 時這將成爲一個難題。)
2、SMTP
簡單郵件傳輸協議(Simple Mail Transfer Protocol,SMTP)由 RFC 821 定義。它定義了發送電子郵件的機制。在 JavaMail API 環境中,您基於 JavaMail 的程序將和您的公司或因特網服務供應商的(Internet Service Provider's,ISP's)SMTP 服務器通訊。SMTP 服務器會中轉消息給接收方 SMTP 服務器以便最終讓用戶經由 POP 或 IMAP 得到。這不是要求 SMTP 服務器成爲開放的中繼,儘管 SMTP 服務器支持身份驗證,不過仍是得確保它的配置正確。像配置服務器來中繼消息或添加刪除郵件帳號這類任務的實現,JavaMail API 中並不支持。
3、POP
POP 表明郵局協議(Post Office Protocol)。目前用的是版本 3,也稱 POP3,RFC 1939 定義了這個協議。POP 是一種機制,因特網上大多數人用它獲得郵件。它規定每一個用戶一個郵箱的支持。這就是它所能作的,而這也形成了許多混淆。使用 POP 時,用戶熟悉的許多性能並非由 POP 協議支持的,如查看有幾封新郵件消息這一性能。這些性能內建於如 Eudora 或 Microsoft Outlook 之類的程序中,它們能記住一些事,諸如最近一次收到的郵件,還能計算出有多少是新的。因此當使用 JavaMail API 時,若是您想要這類信息,您就必須本身算。
4、IMAP
IMAP 是更高級的用於接收消息的協議。在 RFC 2060 中被定義,IMAP 表明因特網消息訪問協議(Internet Message Access Protocol),目前用的是版本 4,也稱 IMAP4。在用到 IMAP 時,郵件服務器必需支持這個協議。不能僅僅把使用 POP 的程序用於 IMAP,並期望它支持 IMAP 全部性能。假設郵件服務器支持 IMAP,基於 JavaMail 的程序能夠利用這種狀況 — 用戶在服務器上有多個文件夾(folder),而且這些文件夾能夠被多個用戶共享。
由於有這一更高級的性能,您也許會認爲全部用戶都會使用 IMAP。事實並非這樣。要求服務器接收新消息,在用戶請求時發送到用戶手中,還要在每一個用戶的多個文件夾中維護消息。這樣雖然能將消息集中備份,但隨着用戶長期的郵件夾愈來愈大,到磁盤空間耗盡時,每一個用戶都會受到損失。使用 POP,就能卸載郵件服務器上保存的消息了。
5、MIME
MIME 表明多用途因特網郵件擴展標準(Multipurpose Internet Mail Extensions)。它不是郵件傳輸協議。但對傳輸內容的消息、附件及其它的內容定義了格式。這裏有不少不一樣的有效文檔:RFC 82二、RFC 204五、RFC 2046 和 RFC 2047。做爲一個 JavaMail API 的用戶,您一般沒必要對這些格式操心。不管如何,必定存在這些格式並且程序會用到它。
6、NNTP及其餘
由於 JavaMail API 將供應商和全部其它的東西分開了,您就能輕鬆添加額外的協議支持。Sun 保留了一張第三方供應商列表,他們利用了 Sun 不提供超出(out-of-the-box)支持範圍的協議。您會找到 NNTP(網絡新聞傳輸協議)[新聞組]、S/MIME(安全多用途因特網郵件擴展)及其它支持。
3、JavaMail的關鍵對象:
JavaMail對收發郵件進行了高級的抽象,造成了一些關鍵的的接口和類,它們構成了程序的基礎,下面咱們分別來了解一下這些最多見的對象。
Properties:屬性對象
因爲JavaMail須要和郵件服務器進行通訊,這就要求程序提供許多諸如服務器地址、端口、用戶名、密碼等信息,JavaMail經過Properties對象封裝這些屬性西信息。以下面的代碼封裝了兩個屬性信息:
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.sina.com.cn");
props.put("mail.smtp.auth", "true");
針對不一樣的的郵件協議,JavaMail規定了服務提供者必須支持一系列屬性,下表是針對SMTP協議的一些常見屬性(屬性值都以String類型進行設置,屬性類型欄僅表示屬性是如何被解析的):
屬性名 |
屬性類型 |
說明 |
mail.stmp.host |
String |
SMTP服務器地址,如smtp.sina.com.cn |
mail.stmp.port |
int |
SMTP服務器端口號,默認爲25 |
mail.stmp.auth |
boolean |
SMTP服務器是否須要用戶認證,默認爲false |
mail.stmp.user |
String |
SMTP默認的登錄用戶名 |
mail.stmp.from |
String |
默認的郵件發送源地址 |
mail.stmp.socketFactory.class |
String |
socket工廠類類名,經過設置該屬性能夠覆蓋提供者默認的實現,必須實現javax.net.SocketFactory接口 |
mail.stmp.socketFactory.port |
int |
指定socket工廠類所用的端口號,若是沒有規定,則使用默認的端口號 |
mail.smtp.socketFactory.fallback |
boolean |
設置爲true時,當使用指定的socket類建立socket失敗後,將使用java.net.Socket建立socket,默認爲true |
mail.stmp.timeout |
int |
I/O鏈接超時時間,單位爲毫秒,默認爲永不超時 |
其餘幾個協議也有相似的一系列屬性,如POP3的mail.pop3.host、mail.pop3.port以及IMAP的mail.imap.host、mail.imap.port等。更詳細的信息請查看com.sun.mail.smtp、com.sun.mail.pop3和com.sun.mail.imap這三個包的Javadoc:http://java.sun.com/products/javamail/javadocs/index.html。
Session:會話對象
Session是一個很容易被誤解的類,這歸咎於混淆視聽的類名。千萬不要覺得這裏的Session像HttpSession同樣表明真實的交互會話,但建立Session對象時,並無對應的物理鏈接,它只不過是一對配置信息的集合。Session的主要做用包括兩個方面:
1)接收各類配置屬性信息:經過Properties對象設置的屬性信息;
2)初始化JavaMail環境:根據JavaMail的配置文件,初始化JavaMail環境,以便經過Session對象建立其餘重要類的實例。
因此,若是把Session改名爲Configure也許更容易理解一些。JavaMail提供者在Jar包的META-INF目錄下,經過如下文件提供了基本配置信息,以便session可以根據這個配置文件加載提供者的實現類:
l javamail.providers和javamail.default.providers;
l javamail.address.map和javamail.default.address.map。
下面是Sun提供者java.mail.default.providers文件的配置信息(位於mail.jar中):
# JavaMail IMAP provider Sun Microsystems, Inc
protocol=imap; type=store; class="com".sun.mail.imap.IMAPStore; vendor=Sun Microsystems, Inc;
protocol=imaps; type=store; class="com".sun.mail.imap.IMAPSSLStore; vendor=Sun Microsystems, Inc;
# JavaMail SMTP provider Sun Microsystems, Inc
protocol=smtp; type=transport; class="com".sun.mail.smtp.SMTPTransport; vendor=Sun Microsystems, Inc;
protocol=smtps; type=transport; class="com".sun.mail.smtp.SMTPSSLTransport; vendor=Sun Microsystems, Inc;
# JavaMail POP3 provider Sun Microsystems, Inc
protocol=pop3; type=store; class="com".sun.mail.pop3.POP3Store; vendor=Sun Microsystems, Inc;
protocol=pop3s; type=store; class="com".sun.mail.pop3.POP3SSLStore; vendor=Sun Microsystems, Inc;
這個配置文件提供瞭如下四個方面的信息:
protocol:協議名稱;
type:協議類型;
class:對應該操做類型的實現類;
vendor:廠商名稱。
Session在加載配置文件時會按照如下優先級順序進行:
1)首先使用<JAVA_HOME>/lib中的javamail.providers;
2)若是1)不存在相應的配置文件,使用類路徑下mail.jar中META-INF目錄下的javamail.providers;
3)若是2)不存在相應的配置文件,使用類路徑下的mail.jar中META-INF目錄下的javamail.default.providers;
因此開發者能夠在<JAVA_HOME>/lib目錄下提供配置文件覆蓋mail.jar/META-INF目錄中廠商的配置。可是,通常狀況下,咱們無須這樣作。
Session經過JavaMail配置文件以及程序中設置的Properties對象構建一個郵件處理環境,後續的處理將在Session基礎上進行。Session擁有多個靜態工廠方法用於建立Session實例。
l static Session getDefaultInstance(Properties props, Authenticator authenticator):當JVM中已經存在默認的Session實例中,直接返回這個實例,不然建立一個新的Session實例,並將其做爲JVM中默認Session實例。這個API很詭異,咱們將對它進行詳細的講解。因爲這個默認Session實例能夠被同一個JVM全部的代碼訪問到,而Session中自己又可能包括密碼、用戶名等敏感信息在內的全部屬性信息,因此後續調用也必須傳入和第一次相同的Authenticator實例,不然將拋出java.lang.SecurityException異常。若是第一次調用時Authenticator入參爲null,則後續調用經過null的Authenticator入參或直接使用getDefaultInstance(Properties props)便可返回這個默認的Session實例。值得一提的是,雖而後續調用也會傳入Properties,但新屬性並不會起做用,若是但願採用新的屬性值,則能夠經過getDefaultInstance(Properties props)建立一個新的Session實例達到目的。Authenticator在這裏承當了兩個功能:首先,對JVM中默認Session實例進行認證保護,後續調用執行getDefaultInstance(Properties props, Authenticator authenticator)方法時必須和第一次同樣;其次,在具體和郵件服務器交互時,又做爲認證的信息;
l static Session getDefaultInstance(Properties props):返回JVM中默認的Session實例,若是第一次建立Session未指定Authenticator入參,後續調用可使用該訪問獲取Session;
l static Session getInstance(Properties props, Authenticator authenticator):建立一個新的Session實例,它不會在JVM中被做爲默認實例共享;
l static Session getInstance(Properties props):根據相關屬性建立一個新的Session實例,未使用安全認證信息;
Session是JavaMail提供者配置文件以及設置屬性信息的「容器」,Session自己不會和郵件服務器進行任何的通訊。因此在通常狀況下,咱們僅須要經過getDefaultInstance()獲取一個共享的Session實例就能夠了,下面的代碼建立了一個Session實例:
Properties props = System.getProperties();
props.setProperty("mail.transport.protocol", "smtp"); …
Session session = Session.getDefaultInstance(props);
Transport和Store:傳輸和存儲
郵件操做只有發送或接收兩種處理方式,JavaMail將這兩種不一樣操做描述爲傳輸(javax.mail.Transport)和存儲(javax.mail.Store),傳輸對應郵件的發送,而存儲對應郵件的接收。
Session提供了幾個用於建立Transport和Store實例的方法,在具體講解這些方法以前,咱們事先了解一下Session建立Transport和Store的內部機制。咱們知道提供者在javamail.providers配置文件中爲每一種支持的郵件協議定義了實現類,Session根據協議類型(stmp、pop3等)和郵件操做方式(傳輸和存儲)這兩個信息就能夠定位到一個實例類上。好比,指定stmp協議和transport類型後,Session就會使用com.sun.mail.smtp.SMTPTransport實現類建立一個Transport實例,而指定pop3協議和store類型時,則會使用com.sun.mail.pop3.POP3Store實例類建立一個Store實例。Session提供了多個重載的getTransport()和getStore()方法,這些方法將根據Session中Properties屬性設置狀況進行工做,影響這兩套方法工做的屬性包括:
屬性名 |
說明 |
mail.transport.protocol |
默認的郵件傳輸協議,例如,smtp |
mail.store.protocol |
默認的存儲郵件協議,例如:pop3 |
mail.host |
默認的郵件服務地址,例如:192.168.67.1 |
mail.user |
默認的登錄用戶名,例如:zapldy |
下面,咱們再回頭來了解Session的getTransport()和getStore()的重載方法。
l Transport getTransport():當Session實例設置了mail.transport.protocol屬性時,該方法返回對應的Transport實例,不然拋出javax.mail.NoSuchProviderException。
l Transport getTransport(String protocol):若是Session沒有設置mail.transport.protocol屬性,能夠經過該方法返回指定類型的Transport,如transport = session.getTransport(「smtp」)。
若是Session中未包含Authenticator,以上兩方法建立的Transport實例和郵件服務器交互時必須顯示提供用戶名/密碼的認證信息。若是Authenticator非空,則能夠在和郵件服務器交互時被做爲認證信息使用。除了以上兩種提供認證信息的方式外,Session還可使用如下的方法爲Transport提供認證信息。
Transport getTransport(URLName url):用戶能夠經過URLName入參指定郵件協議、郵件服務器、端口、用戶名和密碼信息,請看下面的代碼:
URLName urln = new URLName(「smtp」, 「smtp.sina.com.cn」, 25, null, 「masterspring2」, 「spring」);
Transport transport = session.getTransport(urln);
這裏,指定了郵件協議爲smtp,郵件服務器是smtp.sina.com.cn,端口爲25,用戶名/密碼爲masterspring2/spring。
消息發送的最後一部分是使用 Transport 類。這個類用協議指定的語言發送消息(一般是 SMTP)。它是抽象類,它的工做方式與 Session 有些相似。僅調用靜態 send() 方法,就能使用類的 缺省 版本:
Transport.send(message);
或者,您也能夠從針對您的協議的會話中得到一個特定的實例,傳遞用戶名和密碼(若是沒必要要就不傳),發送消息,而後關閉鏈接。
message.saveChanges(); // implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
後面這種方法在您要發送多條消息時最好,由於它能保持郵件服務器在消息間的活動狀態。基本 send() 機制爲每一個方法的調用設置與服務器獨立的鏈接。
注意:要觀察傳到郵件服務器上的郵件命令,請用 session.setDebug(true) 設置調試標誌。
用 Session 獲取消息與發送消息開始很類似。可是,在 session 獲得後,極可能使用用戶名和密碼或使用 Authenticator 鏈接到一個 Store。相似於 Transport ,您告知 Store 使用什麼協議:
// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);
鏈接到 Store 以後,接下來,您就能夠獲取一個 Folder,您必需先打開它,而後才能讀裏面的消息。
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
POP3 惟一能夠用的文件夾是 INBOX。若是使用 IMAP,還能夠用其它文件夾。
注意:Sun 的供應商有意變得聰明。雖然 Message message[] = folder.getMessages(); 看上去是個很慢的操做,它從服務器上讀取每一條消息,但僅在你實際須要消息的一部分時,消息的內容纔會被檢索。
一旦有了要讀的 Message,您能夠用 getContent() 來獲取其內容,或者用 writeTo() 將內容寫入流。getContent() 方法只能獲得消息內容,而 writeTo() 的輸出卻包含消息頭。
System.out.println(((MimeMessage)message).getContent());
一旦讀完郵件,要關閉與 folder 和 store 的鏈接。
folder.close(aBoolean);
store.close();
傳遞給 folder 的 close() 方法的 boolean 表示是否清除已刪除的消息從而更新 folder。
Message:消息對象
一旦得到 Session 對象,就能夠繼續建立要發送的消息。這由 Message 類來完成。由於 Message 是個抽象類,您必需用一個子類,多數狀況下爲 javax.mail.internet.MimeMessage。MimeMessage 是個能理解 MIME 類型和頭的電子郵件消息,正如不一樣 RFC 中所定義的。雖然在某些頭部域非 ASCII 字符也能被譯碼,但 Message 頭只能被限制爲用 US-ASCII 字符。
要建立一個 Message,請將 Session 對象傳遞給 MimeMessage 構造器:
MimeMessage message = new MimeMessage(session);
注意:還存在其它構造器,如用按 RFC822 格式的輸入流來建立消息。
一旦得到消息,您就能夠設置各個部分,由於 Message 實現 Part 接口(且 MimeMessage 實現 MimePart )。設置內容的基本機制是 setContent() 方法,同時使用參數,分別表明內容和 mime 類型:
message.setContent("Hello", "text/plain");
但若是,您知道您在使用 MimeMessage,並且消息是純文本格式,您就能夠用 setText() 方法,它只須要表明實際內容的參數,( MIME 類型缺省爲 text/plain):
message.setText("Hello");
後一種格式是設置純文本消息內容的首選機制。至於發送其它類型的消息,如 HTML 文件格式的消息,咱們首選前者。
用 setSubject() 方法設置 subject(主題):
message.setSubject("First");
下面的代碼演示了建立一個簡單郵件信息的過程:
Message msg = new MimeMessage(session);
msg.setSubject("Test Title");
msg.setText("How are you!");
msg.setSentDate(new Date());
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");
須要爲消息的 from 域和 to 域建立地址對象。除非郵件服務器阻止,沒什麼能阻止你發送一段看上去是來自任何人的消息。
一旦建立了 address(地址),將它們與消息鏈接的方法有兩種。若是要識別發件人,您能夠用 setFrom() 和 setReplyTo() 方法。
message.setFrom(address)
須要消息顯示多個 from 地址,可使用 addFrom() 方法:
Address address[] = ...;
message.addFrom(address);
若要識別消息 recipient(收件人),您可使用 addRecipient() 方法。除 address(地址)外,這一方法還請求一個 Message.RecipientType。
message.addRecipient(type, address)
三種預約義的地址類型是:
Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC
若是消息是發給副總統的,同時發送一個副本(carbon copy)給總統夫人,如下作法比較恰當:
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 沒有提供電子郵件地址有效性覈查機制。雖然經過編程,本身可以掃描有效字符(如 RFC 822 中定義的)或驗證郵件交換(mail exchange,MX)記錄,但這些功能不屬於 JavaMail API。
Authenticator:認證者
與 java.net 類同樣,JavaMail API 也能夠利用 Authenticator 經過用戶名和密碼訪問受保護的資源。對於JavaMail API 來講,這些資源就是郵件服務器。JavaMail Authenticator 在 javax.mail 包中,並且它和 java.net 中同名的類 Authenticator 不一樣。二者並不共享同一個 Authenticator,由於JavaMail API 用於 Java 1.1,它沒有 java.net 類別。
要使用 Authenticator,先建立一個抽象類的子類,並從 getPasswordAuthentication() 方法中返回 PasswordAuthentication 實例。建立完成後,您必需向 session 註冊 Authenticator。而後,在須要認證的時候,就會通知 Authenticator。您能夠彈出窗口,也能夠從配置文件中(雖然沒有加密是不安全的)讀取用戶名和密碼,將它們做爲 PasswordAuthentication 對象返回給調用程序。
Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
發送消息:
發送電子郵件消息這一過程包括獲取一個會話,建立並填充一則消息,而後發送。獲得 Session 時,經由設置傳遞的 Properties 對象中的 mail.smtp.host 屬性,能夠指定您的 SMTP 服務器:
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 程序塊中,這樣建立和發送消息時就可以拋出異常。
消息的提取:
爲讀郵件,您獲取一個會話,獲取並鏈接一個用於郵箱的適宜的存儲(store),打開適宜的文件夾,而後獲取您的消息。一樣,切記完成後關閉鏈接。
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<n; i++) {
System.out.println(i + ": " + message[i].getFrom()[0]
+ "/t" + message[i].getSubject());
}
// Close connection
folder.close(false);
store.close();
對每條消息作些什麼由您決定。上面的代碼塊只是顯示這些消息的發件人和主題。技術上講,from 地址列表可能爲空,而 getFrom()[0] 調用會拋出一個異常。
要顯示所有信息,您能夠在用戶看完 from 和 subject 域以後給出提示,如用戶有須要,就調用消息的 writeTo() 方法來實現。
BufferedReader reader = new BufferedReader (
new InputStreamReader(System.in));
// Get directory
Message message[] = folder.getMessages();
for (int i=0, n=message.length; i<n; 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;
}
}
消息和標識的刪除:
消息的刪除涉及使用與消息相關的 Flags(標誌)。不一樣 flag 對應不一樣的狀態,有些由系統定義而有些則由用戶定義。下面列出在內部類 Flags.Flag 中預約義的標誌:
* Flags.Flag.ANSWERED
* Flags.Flag.DELETED
* Flags.Flag.DRAFT
* Flags.Flag.FLAGGED
* Flags.Flag.RECENT
* Flags.Flag.SEEN
* Flags.Flag.USER
僅僅由於存在一個標誌,並不意味着全部郵件服務器或供應商都支持這個標誌。例如,除了刪除消息標誌外,POP 協議再也不支持其它任何標誌。檢查是否存在新郵件,這不是個 POP 任務,而是內建於郵件客戶機的任務。爲找出哪些標誌能被支持,能夠用 getPermanentFlags() 向 folder 提出要求。
要刪除消息,您能夠設置消息的 DELETED flag:
message.setFlag(Flags.Flag.DELETED, true);
首先,請以 READ_WRITE 模式打開 folder:
folder.open(Folder.READ_WRITE);
而後,當全部消息的處理完成後,關閉 folder,並傳遞一個 true 值,從而擦除(expunge)有 delete 標誌的消息。
folder.close(true);
一個 Folder 的 expunge() 方法能夠用來刪除消息。但 Sun 的 POP3 供應商不支持。其它供應商有的或許可以實現這一功能,而有的則不能。IMAP 供應商極有可能實現此功能。由於 POP 只支持單個對郵箱的訪問,對 Sun 的供應商來講,您必需關閉 folder 以刪除消息。
要取消標誌,只要傳遞 false 給 setFlag() 方法就好了。想知道是否設置過標誌,能夠用 isSet() 檢查。
親自認證:
您已經知道 — 若是須要能夠用一個 Authenticator 提示用戶輸入用戶名和密碼,而不是將用戶名和密碼做爲字符串傳遞。在這裏您會明確瞭解怎樣更充分的使用認證。
不用主機、用戶名和密碼與 Store 相鏈接,而是設置 Properties 來擁有主機,而後告訴 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();
而後,您建立一個 Authenticator 子類並從 getPasswordAuthentication() 方法中返回 PasswordAuthentication 對象。下面就是這樣一種實現,其中用戶名和密碼僅佔用一個域。(這不是一個 Swing 工程教程;只要將兩部分輸入同一個域,用逗號分隔就行。)
import javax.mail.*;
import javax.swing.*;
import java.util.*;
public class PopupAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
String username, password;
String result = JOptionPane.showInputDialog(
"Enter 'username,password'");
StringTokenizer st = new StringTokenizer(result, ",");
username = st.nextToken();
password = st.nextToken();
return new PasswordAuthentication(username, password);
}
}
由於 PopupAuthenticator 涉及到 Swing,它會啓動 AWT 的事件處理線程。這一點基本上要求您在代碼中添加一個對 System.exit() 的調用來終止程序。
消息的回覆:
Message 類引入一個 reply() 方法來配置一個新 Message,包括正確的 recipient(收件人)和添加「Re」(若是沒有就添加)的正確的 subject。這樣作並無爲消息添加新內容,僅僅將 from 或 reply-to(被回覆人) 頭複製給新的收件人。這種方法用一個 boolean 參數指定消息只回復給發件人(false)或回覆給全體(true)。
MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);
在發送消息時要配置 reply to(被回覆人) 地址,能夠用 setReplyTo() 方法。
消息的轉發:
轉發消息有一點棘手。沒有單獨的方法能夠調用,您經過對組成消息各部分的處理來組織要轉發的消息。
一條郵件消息能夠由多個部分組成。在處理 MIME 消息時,消息中每部分都是 BodyPart,再特殊些,是 MimeBodyPart。不一樣的 body part(信體部件或正文部件)結合成一個容器,名爲 Multipart,再特殊些,就是 MimeMultipart。要轉發一條消息,您爲本身的消息正文建立一個部件,要轉發的消息做爲另外一部件。而且將兩個部件結合成一個 multipart(多部件)。而後您將這個 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
Transport.send(forward);
附件的處理:
附件是郵件消息的相關資源,如一般不包含在消息正文裏文本文件、電子表格或圖像等。常見的郵件程序,如 Eudora 和 pine 之類,能夠用 JavaMail API 將資源 attach(附加) 到您的消息上,就能夠在收到消息時獲得。
附件的發送:
發送附件很是像轉發消息。您創建各部分以組成完整消息。完成第一部件,即消息正文後,您添加其它部件,其中每一個 DataHandler 都表明附件,而不是轉發消息狀況下的共享處理程序。若是從文件中讀附件,附件的數據源是 FileDataSource。而若是從 URL 中讀時,附件的數據源是 URLDataSource。一旦存在 DataSource,只要先把它傳遞給 DataHandler 構造器,最後再用 setDataHandler() 把它附加到 BodyPart。假定您要保留附件的原始文件名,最終要作的是用 BodyPart 的 setFileName() 方法設置與附件相關的文件名。以下所示:
// Define message
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
message.setSubject("Hello JavaMail Attachment");
// Create the message part
BodyPart messageBodyPart = new MimeBodyPart();
// Fill the message
messageBodyPart.setText("Pardon Ideas");
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);
// Part two is attachment
messageBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);
// Put parts in message
message.setContent(multipart);
// Send the message
Transport.send(message);
就消息引入附件時,若程序是個 servlet (小服務程序),除告知消息發送到何處外,還必需上載附件。能夠將 multipart/form-data 表單編碼類型(form encoding type)用於每一個上載文件的處理。
<FORM ENCTYPE="multipart/form-data"
method=post action="/myservlet">
<INPUT TYPE="file" NAME="thefile">
<INPUT TYPE="submit" VALUE="Upload">
</FORM>
注意:消息大小由 SMTP 服務器而不是 JavaMail API 來限制。若是您碰到問題,能夠考慮用設置 ms 和 mx 參數的方法增大 Java 堆大小。
附件的獲取:
從消息中獲取附件比發送它們棘手些,由於 MIME 沒有簡單的關於附件的概念。當消息包含附件時,消息的內容是個 Multipart 對象。接着,您須要處理每一個 Part,獲取主要內容和附件。標有從 part.getDisposition() 得到的 Part.ATTACHMENT 配置(disposition)的部件(Part)無疑就是附件。可是,沒有配置(以及一個非文本 MIME 類型)和帶 Part.INLINE 配置的部件也多是附件。當配置要麼是 Part.ATTACHMENT,要麼是 Part.INLINE 時,這個消息部件的內容就能被保存。只要用 getFileName() 和 getInputStream() 就能分別獲得原始文件名和輸入流。
Multipart mp = (Multipart)message.getContent();
for (int i=0, n=multipart.getCount(); i<n; i++) {
Part part = multipart.getBodyPart(i));
String disposition = part.getDisposition();
if ((disposition != null) &&
((disposition.equals(Part.ATTACHMENT) ||
(disposition.equals(Part.INLINE))) {
saveFile(part.getFileName(), part.getInputStream());
}
}
saveFile() 方法僅依據文件名建立了一個 File,它從輸入流中將字節讀出,而後寫入到文件中。萬一文件已經存在,就在文件名後添加一個數字做爲新文件名,若是這個文件名仍存在,則繼續添,直到找不到這樣的文件名爲止。
// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
file = new File(filename+i);
}
上面的代碼涵蓋了最簡單的狀況 — 消息中各部件恰當的標記了。要涵蓋全部狀況,還要在配置爲空時進行處理,而且獲取部件的 MIME 類型來進行相應處理。
if (disposition == null) {
// Check if plain
MimeBodyPart mbp = (MimeBodyPart)part;
if (mbp.isMimeType("text/plain")) {
// Handle plain
} else {
// Special non-attachment cases here of image/gif, text/html, ...
}
...
}
對 HTML 消息的處理
發送基於 HTML 文件格式消息的工做量比發送純文本消息多,雖然不必定非要這些多餘的工做量。如何選擇徹底取決於給定的請求。
HTML 消息的發送:
若您所要作的所有事情是發送一份 HTML 文件的等價物做爲消息,但讓郵件閱讀者爲不能提取任何內嵌圖像或相關片斷而擔憂的話,可使用 Message 的 setContent() 方法,把內容看成一個 String 傳入,並將內容類型設置成 text/html。
String htmlText = "<H1>Hello</H1>" +
"<img src=/"http://www.jguru.com/images/logo.gif/">";
message.setContent(htmlText, "text/html"));
在接收端,若是您用 JavaMail API 提取消息,API 中沒有內建的顯示 HTML 消息的東西。 JavaMail API 只把它當作一串字節流。要顯示 HTML 文件格式的消息,您必需使用 Swing JEditorPane 或其它第三方 HTML 格式查看器組件。
if (message.getContentType().equals("text/html")) {
String content = (String)message.getContent();
JFrame frame = new JFrame();
JEditorPane text = new JEditorPane("text/html", content);
text.setEditable(false);
JScrollPane pane = new JScrollPane(text);
frame.getContentPane().add(pane);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.show();
}
在消息中引入圖像:
另外一方面,若是您想讓 HTML 文件格式內容的消息完整(內嵌的圖像做爲消息的一部分),您必需把圖像做爲附件,而且用一個給定的 cid URL 引用圖像,其中 cid 是圖像附件 Content-ID 頭的引用。
嵌入圖像的過程與附加文件到消息的過程很是類似,惟一的區別在於您必需經過設置 MimeMultipart 構造器中的子類型(或者說用 setSubType())告知 MimeMultipart 各個相關部件,而且將這個圖像的 Content-ID 頭設置成隨機字符串,做爲圖像的 src 在 img 標記中使用。完整的演示以下。
String file = ...;
// Create the message
Message message = new MimeMessage(session);
// Fill its headers
message.setSubject("Embedded Image");
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
String htmlText = "<H1>Hello</H1>" +
"<img src=/"cid:memememe/">";
messageBodyPart.setContent(htmlText, "text/html");
// Create a related multi-part to combine the parts
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);
// Create part for the image
messageBodyPart = new MimeBodyPart();
// Fetch the image and associate to part
DataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","memememe");
// Add part to multi-part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message.setContent(multipart);