JavaMail實現收發郵件——(一)相關概念概述

一、JavaMail概述:

       JavaMail是由Sun定義的一套收發電子郵件的API,不同的廠商可以提供自己的實現類。但它並沒有包含在JDK中,而是作爲JavaEE的一部分。

       廠商所提供的JavaMail服務程序可以有選擇地實現某些郵件協議,常見的郵件協議包括:

l         SMTP:簡單郵件傳輸協議,用於發送電子郵件的傳輸協議;

l         POP3:用於接收電子郵件的標準協議;

l         IMAP:互聯網消息協議,是POP3的替代協議。

這三種協議都有對應SSL加密傳輸的協議,分別是SMTPSPOP3SIMAPS

JavaMail服務提供程序之外,JavaMail還需要JAF(JavaBeans Activation Framework)來處理不是純文本的郵件內容,這包括MIME(多用途互聯網郵件擴展)、URL頁面和文件附件等內容。


二、對相關協議的回顧:

       1、介紹

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

    * SMTP

    * POP

    * IMAP

    * MIME

 

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

      

       2SMTP

       簡單郵件傳輸協議(Simple Mail Transfer ProtocolSMTP)由 RFC 821 定義。它定義了發送電子郵件的機制。在JavaMail API 環境中,您基於 JavaMail 的程序將和您的公司或因特網服務供應商的(Internet Service Provider'sISP'sSMTP服務器通信。SMTP 服務器會中轉消息給接收方 SMTP 服務器以便最終讓用戶經由 POP  IMAP 獲得。這不是要求 SMTP 服務器成爲開放的中繼,儘管 SMTP 服務器支持身份驗證,不過還是得確保它的配置正確。像配置服務器來中繼消息或添加刪除郵件賬號這類任務的實現,JavaMail API 中並不支持。

 

       3POP

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

 

       4IMAP

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

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

      

       5MIME

       MIME 代表多用途因特網郵件擴展標準(Multipurpose Internet Mail Extensions)。它不是郵件傳輸協議。但對傳輸內容的消息、附件及其它的內容定義了格式。這裏有很多不同的有效文檔:RFC 822RFC 2045RFC 2046  RFC 2047。作爲一個JavaMail API 的用戶,您通常不必對這些格式操心。無論如何,一定存在這些格式而且程序會用到它。

 

       6NNTP及其他

       因爲 JavaMail API 將供應商和所有其它的東西分開了,您就能輕鬆添加額外的協議支持。Sun 保留了一張第三方供應商列表,他們利用了 Sun 不提供超出(out-of-the-box)支持範圍的協議。您會找到 NNTP(網絡新聞傳輸協議)[新聞組]S/MIME(安全多用途因特網郵件擴展)及其它支持。


三、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("[email protected]");

 

若希望名字緊挨着電子郵件顯示,也可以把它傳遞給構造器:

 

Address address = new InternetAddress("[email protected]", "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("[email protected]");

Address ccAddress = new InternetAddress("[email protected]");

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("[email protected]"));

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章