JavaMail發送和接收郵件

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、對相關協議的回顧:

       一、介紹

       在研究 JavaMail API 的細則以前,讓咱們回顧用於 API 的協議。基本上,您會逐漸熟悉並喜好的協議有四個:

 

    * SMTP

    * POP

    * IMAP

    * MIME

 

您還將碰到 NNTP 和其它協議。理解全部協議的基本知識將有助於您理解如何使用 JavaMail API。雖然不瞭解這些協議您照樣能夠用這個 API,卻不可以克服那些基礎協議的侷限性。若是咱們精選的協議不能支持某種性能,JavaMail API 決不能魔術般的將這種性能添加上去。(您很快就會看到,在處理 POP 時這將成爲一個難題。)

      

       二、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 中並不支持。

 

       三、POP

       POP 表明郵局協議(Post Office Protocol)。目前用的是版本 3,也稱 POP3,RFC 1939 定義了這個協議。POP 是一種機制,因特網上大多數人用它獲得郵件。它規定每一個用戶一個郵箱的支持。這就是它所能作的,而這也形成了許多混淆。使用POP 時,用戶熟悉的許多性能並非由 POP 協議支持的,如查看有幾封新郵件消息這一性能。這些性能內建於如 Eudora 或Microsoft Outlook 之類的程序中,它們能記住一些事,諸如最近一次收到的郵件,還能計算出有多少是新的。因此當使用JavaMail API 時,若是您想要這類信息,您就必須本身算。

 

       四、IMAP

       IMAP 是更高級的用於接收消息的協議。在 RFC 2060 中被定義,IMAP 表明因特網消息訪問協議(Internet Message Access Protocol),目前用的是版本 4,也稱 IMAP4。在用到 IMAP 時,郵件服務器必需支持這個協議。不能僅僅把使用POP 的程序用於 IMAP,並期望它支持 IMAP 全部性能。假設郵件服務器支持 IMAP,基於 JavaMail 的程序能夠利用這種狀況 — 用戶在服務器上有多個文件夾(folder),而且這些文件夾能夠被多個用戶共享。

       由於有這一更高級的性能,您也許會認爲全部用戶都會使用 IMAP。事實並非這樣。要求服務器接收新消息,在用戶請求時發送到用戶手中,還要在每一個用戶的多個文件夾中維護消息。這樣雖然能將消息集中備份,但隨着用戶長期的郵件夾愈來愈大,到磁盤空間耗盡時,每一個用戶都會受到損失。使用 POP,就能卸載郵件服務器上保存的消息了。

      

       五、MIME

       MIME 表明多用途因特網郵件擴展標準(Multipurpose Internet Mail Extensions)。它不是郵件傳輸協議。但對傳輸內容的消息、附件及其它的內容定義了格式。這裏有不少不一樣的有效文檔:RFC 82二、RFC 204五、RFC 2046 和 RFC 2047。做爲一個 JavaMail API 的用戶,您一般沒必要對這些格式操心。不管如何,必定存在這些格式並且程序會用到它。

 

       六、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);



參考:http://blog.csdn.net/zapldy/article/details/3971579

相關文章
相關標籤/搜索