JavaMail API是一種可選的、能用于讀取、編寫和發送電子消息的包(標準擴展)。您可使用這種包創建郵件用戶代理(Mail User Agent ,MUA) 類型的程序,它類似于Eudora、Pine及Microsoft Outlook這些郵件程序。其主要目的不是像發送郵件或其他郵件傳輸代理(Mail Transfer Agent,MTA)類型的程序那樣用于傳輸、發送和轉發消息。換句話說,用戶可以與MUA類型的程序交互,以閱讀和撰寫電子郵件。MUA依靠MTA處理實際的發送任務。


JavaMail API簡介

JavaMail API是一種可選的、能用于讀取、編寫和發送電子消息的包(標準擴展)。您可使用這種包創建郵件用戶代理(Mail User Agent ,MUA) 類型的程序,它類似于Eudora、Pine及Microsoft Outlook這些郵件程序。其主要目的不是像發送郵件或其他郵件傳輸代理(Mail Transfer Agent,MTA)類型的程序那樣用于傳輸、發送和轉發消息。換句話說,用戶可以與MUA類型的程序交互,以閱讀和撰寫電子郵件。MUA依靠MTA處理實際的發送任務。

JavaMail API的設計是,為收發信息提供與協議無關的訪問。方式是把該API劃分成兩個部分:

· 該API的第一個部分是本課程的重點。基本上是如何發送和接收獨立于提供程序/協議的消息。

· 第二個部分則使用特定的協議語言,如:SMTP、POP、IMAP和NNTP。如果要讓JavaMail API與服務器通信,就需要為之提供協議。由于Sun公司對特定協議提供程序有充分的介紹,用戶可以免費獲取,所以本課程沒有介紹創建特定協議提供程序的內容。

復習相關協議

在學習JavaMail API的深層知識之前,讓我們回過頭來看一看在該API中使用的協議,本質上有4種人們常用的協議:

· SMTP

· POP

· IMAP

· MIME

您還需要了解NNTP及其他一些協議。理解這些協議的基本原理有助于您理解如何使用JavaMail API。而該API的設計要與協議無關,所以不能克服這些基礎協議的限制。如果選用的協議不支持某種功能,那么JavaMail API也無法在其上添加這種功能。(正如您一會兒就會看到的,在操作POP協議時,常常會碰到這種問題)。

SMTP

簡單郵件傳輸協議(SMTP)是用于傳送電子郵件的機制。在JavaMail API環境中,您的基于JavaMail的程序將與您公司或Internet服務提供商(ISP)的SMTP服務器通信。該SMTP服務器將會把消息轉發給用作接收消息的SMTP服務器,最后用戶可通過POP或IMAP協議獲取該消息。由于支持身份驗證,所以不需要SMTP服務器是一種開放的轉發器,但需要確保SMTP服務器配置正確。JavaMail API中沒有集成用于處理諸如配置服務器以轉發消息或添加/刪除電子郵件帳戶這一類任務的功能。

POP

POP的含義是郵局協議,當前的版本為3,也稱作POP3,該協議是在RFC 1939中定義的。POP是Internet上的大多數人用來接收郵件的機制。它為每個用戶的每個郵箱定義支持,這是它所做的全部工作,也是大多數問題的根源。在使用POP協議時,人們熟悉的很多功能,如查看收到了多少新郵件消息的功能,POP根本不支持。這些功能都內置到諸如Eudora或Microsoft Outlook之類的郵件程序中,能為您記住接收的上一封郵件,以及計算有多少新郵件這類信息。因此,使用JavaMail API時,如果想獲取這類信息,將需要由自己進行計算。

IMAP

IMAP是用于接收消息的更加高級的協議,它是在RFC 2060中定義的。IMAP的含義是"Internet消息訪問協議",當前版本是第4版,也稱作IMAP4。使用IMAP時,您的郵件服務器必須支持該協議。您不能只是簡單地把程序轉變為支持IMAP,而不是支持POP,就指望能支持IMAP中的一切。假定您的郵件服務器支持IMAP,那么基于JavaMail的程序就可利用在服務器上擁有多個文件夾的用戶,并且這些文件夾可以被多個用戶共享的功能。

由于IMAP協議具有更高級的功能,您也許會想IMAP應該被每一個人使用,但事實不是這樣。因為IMAP會加重郵件服務器的負荷,它需要服務器接收新消息,發送消息給請求的用戶,并在多個文件夾中為每個用戶維護這些消息。而這要集中備份,因而長期下去用戶的文件夾會變得越來越大,當磁盤空間用光了時,每個人都會遭受損失。而使用POP協議時,已保存消息可以解除服務器的重負。

MIME

MIME的含義是"多用途的網際郵件擴充協議"。它不是一種郵件傳輸協議,相反,它定義傳輸的內容:消息的格式、附件等。許多文檔都定義了MIME協議,包含:RFC 822、RFC 2045、RFC 2046和RFC 2047。作為JavaMail API的用戶,一般不需要擔心這些格式。但是,這些格式確實存在,并為您的程序所用。

NNP和其他協議

由于JavaMail API分開了提供程序和其他部分,所以您可以輕松地為附加協議添加支持。Sun公司提供第3方提供程序清單,這些提供程序要利用 Sun公司不支持的少見的協議。在這份清單中,您將會看到對NNTP(網絡新聞傳輸協議)[新聞組]、S/MIME(安全多用途的網際郵件擴充協議)及其他協議的提供支持的第3方提供程序。

安裝

目前有兩種版本的JavaMail API最常用:1.2和1.1.3。本課程中的所有例子都適用于這兩種版本。其中JavaMail API 1.2是最新的,而JavaMail API 1.1.3中包含了Java 2企業版(J2EE)平臺1.2.1版,所以它仍然很常用。使用JavaMail API的版本會對您的下載和安裝產生一些影響。這兩種版本的JavaMail API都能與JDK 1.1.6、Java 2標準版(J2SE)平臺1.2.x和1.3.x協同工作。

注意:在安裝了Sun公司的JavaMail工具后,會在演示目錄下看到許多示例程序。

安裝JavaMail 1.2

要使用JavaMail 1.2 API,可以下載JavaMail 1.2工具,然后解壓縮javamail-1_2.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.2工具帶有SMTP、IMAP4和POP3提供程序以及核心類。

安裝完JavaMail 1.2后,再安裝JavaBeans Activation Framework。

安裝JavaMail 1.1.3

要使用JavaMail 1.1.3 API,可以下載JavaMail 1.1.3工具,然后解壓縮javamail1_1_3.zip文件,并把mail.jar文件添加到典型安裝路徑下。JavaMail 1.1.3工具帶有SMTP和IMAP4提供程序以及核心類。

如果您想用JavaMail 1.1.3訪問POP服務器,需要下載并安裝POP3提供程序。Sun公司擁有一個獨立于 JavaMail 工具的提供程序。在下載并解壓縮pop31_1_1.zip文件后,也還需要把pop3.jar添加到典型安裝路徑下。

安裝完JavaMail 1.1.3后,再安裝JavaBeans Activation Framework。

安裝JavaBeans Activation Framework

JavaMail API的所有版本都需要JavaBeans Activation Framework(JavaBeans激活框架),這種框架提供了對輸入任意數據塊的支持,并能相應地對其進行處理。看上去效果好像不太好,但該框架是在當今的許多瀏覽器和郵件工具中可以找到的基本MIME類型支持。下載該框架后,解壓縮jaf1_0_1.zip文件,并將activation.jar文件添加到典型安裝路徑下。

對于JavaMail 1.2用戶,現在應該把mail.jar和activation.jar文件添加到典型安裝路徑下。

對于JavaMail 1.1.3用戶,現在應該把mail.jar、pop3.jar和activation.jar添加到典型安裝路徑下。如果您不打算使用POP3,就不需要把pop3.jar文件添加到典型安裝路徑下。

如果您不想更改安裝路徑環境變量,可以把JAR文件復制到Java運行時環境(JRE)目錄下的lib/ext目錄下。例如,對于J2SE 1.3版本,Windows平臺上的默認目錄應該是C:\jdk1.3\jre\lib\ext。

使用Java 2企業版

如果您使用的是J2EE,則在使用基本JavaMail API時,不需要做什么特殊的工作;JavaMail API帶有J2EE類。只要確保j2ee.jar文件位于典型安裝路徑下,并完成了所有的設置工作。

對于J2EE 1.2.1,POP3提供程序是單獨提供的,因此需要下載該提供程序,并按安裝JavaMail 1.1.3的步驟,在J2EE 1.2.1中包含POP3提供程序。J2EE 1.3的用戶會獲得J2EE和POP3提供程序,因而不需要對POP3提供程序執行獨立安裝。使用這兩種版本的J2EE用戶,都不需要安裝JavaBeans Activation Framework。

練習

設置您的 JavaMail 環境。

復習核心類

在開始深入研究JavaMail類之前,首先讓用戶瀏覽一下構成API的核心類:會話、消息、地址、驗證程序、傳輸,存儲和文件夾。所有這些類都可以在JavaMail API即javax.mail的頂層包中找到,盡管您將頻繁地發現您自己使用的子類是在javax.mail.internet包中找到的。

Session類

Session類定義了一個基本的郵件會話。通過該會話可讓別的工作順利執行。Session對象利用java.util.Properties對象獲取諸如郵件服務器、用戶名、密碼等信息,以及其他可在整個應用程序中共享的信息。

Session類的構造器是私有的。您可以獲得一個可被getDefaultInstance()方法共享的單一的默認會話:

Properties props = new Properties();

// fill props with any information

Session session = Session.getDefaultInstance(props, null);

或者,您可以用getInstance()方法創建一個獨特的會話:

Properties props = new Properties();

// fill props with any information

Session session = Session.getInstance(props, null);

這兩種情形下的null參數都是一種Authenticator對象,它不是在此時使用的。詳細信息請參閱其后的"Autherticator"一節。

在大多數情況下,使用共享會話就足夠了,即使為多個用戶郵箱處理郵件會話也是如此。您可以在通信過程的后面一步添加上用戶名和密碼的組合,并保持所有的一切是獨立的。

Message類

一旦創建了自己的Session對象,就是該去創建要發送的消息的時候了。這時就要用到消息類型。作為一個抽象類,您必須操作一個子類,在大多數情況下,該子類是javax.mail.internet.MimeMessage。一個MimeMessage是一種理解MIME類型和報頭(在不同的RFC文檔中均有定義)的消息。消息的報頭被嚴格限制成只能使用US-ASCII字符,盡管非ASCII字符可以被編碼到某些報頭字段中。

可以通過將Session對象傳遞給MimeMessage構造器的方法來創建消息:

MimeMessage message = new MimeMessage(session);

注意:還有其他的構造器,像用于創建消息的源于RFC822格式化的輸入流的構造器。

一旦創建了消息,就可以設置其各個部分,如Message(消息)實現Part(部分)接口(以MimeMessage實現MimePart)。設置內容的基本機制是setContent()方法,它帶有表示內容和MIME類型的參數:

message.setContent("Hello", "text/plain");

但是,如果正在使用 MimeMessage,并且您的消息是純文本,那么您就可以使用setText()方法。該方法只需要一個表示實際內容的參數,默認的MIME類型為純文本:

message.setText("Hello");

對于純文本消息,setText()方法更常常被用來設置內容。要發送其他類型的消息,如HTML消息,就要使用setContent方法()。現在用的更多的是HTML消息。

要設置主題,可以使用setSubject()方法:

message.setSubject("First");

Address類

一旦創建了會話和消息,并為消息填充了內容,就需要用Address類為您的信件標上地址了。同Message類一樣,Address類也是一種抽象類。您可以使用javax.mail.internet.InternetAddress類。

要創建只帶有電子郵件地址的地址,可以把電子郵件地址傳遞給Address類的構造器:

Address address = new InternetAddress("president@whitehouse.gov");

如果想讓一個名字出現在電子郵件地址后,也可以將其傳遞給構造器:

Address address = new InternetAddress("president@whitehouse.gov", "George Bush");

您要為消息的from(發送者)字段和to(接收者)字段創建地址對象。除非您的郵件服務器阻止這樣做,否則要在發送的消息中注明該消息的發送者。

一旦創建好了地址,有兩種方法可讓您將地址與消息連接起來。為了鑒別發送者,您可以使用setFrom()和setReplyTo()方法。

message.setFrom(address)

如果您的消息需要顯示多個地址來源,則可以使用addFrom()方法:

Address address[] = ...;

message.addFrom(address);

為了鑒別消息接收者,您可以使用addRecipient()方法。該方法除了需要一個地址參數外,還需要一個Message.RecipientType屬性(消息的接收類型)。

message.addRecipient(type, address)

地址的3種預定義類型如下:

· 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沒有提供檢查電子郵件地址有效性的機制。您可以自己編寫支持掃描有效字符(在RFC 822文檔中所定義的)的程序或檢驗MX(郵件交換)記錄,這些都超越了JavaMail API的范圍。

Authenticator類

與java.net類一樣,JavaMail API可以利用Authenticator(驗證程序)類通過用戶名和密碼來訪問受保護的資源。對于JavaMail API來說,這種受保護的資源是指郵件服務器。JavaMail的Authenticator類可以在javax.mail包中找到,并有別于同名的java.net類。當JavaMail API在Java 1.1下工作時,JavaMail和java.net不會共享同一個Authenticator類名稱,這是因為Java 1.1中不含有java.net。

要使用Authenticator類,您可以使用該抽象類的子類,并通過getPasswordAuthentication()方法返回一個PasswordAuthentication實例。在創建時,您必須用會話記錄Authentication類。其后,當需要進行身份驗證時,會通知您的Authenticator。會彈出一個窗口,或從一個配置文件(盡管不加密就不安全)中讀取用戶名和密碼,并把它們作為一個PasswordAuthentication對象返回給調用程序。

Properties props = new Properties();

// fill props with any information

Authenticator auth = new MyAuthenticator();

Session session = Session.getDefaultInstance(props, auth);

Transport類

發送消息的最后一步操作是使用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)方法設置調試標志。

Store和Folder類

使用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();這條語句卻是一種從服務器逐條讀取消息的緩慢操作,所以僅當您確實需要獲取消息部分(該內容是所檢索消息的內容)時可以使用這條語句。

一旦讀取消息,就可以使用getContent()方法獲取其內容,或使用writeTo()方法將其內容寫到一個流中。getContent()方法只獲取消息內容,而writeTo()方法則還會輸出報頭。

System.out.println(((MimeMessage)message).getContent());

一旦您閱讀完郵件,就可以關閉對文件夾和存儲的連接。

folder.close(aBoolean);

store.close();

傳遞給文件夾的close()方法的布爾變量指定了是否通過清除已刪除的消息來更新文件夾。

繼續前進

實際上,理解使用這7個類的方式,是使用JavaMail API處理幾乎所有事情所需要的全部內容。用這7個類以外的方式構建的JavaMail API,其大多數功能都是以幾乎完全相同或特定的方式來執行任務的,就好像內容是附件。特定的任務,如:搜索、隔離等將在后面進行介紹。

使用JavaMail API

您已經看到了如何操作JavaMail API的核心部分。在下面幾節中,您將學習如何連接幾個部分以執行特定的任務。

發送消息

發送電子郵件消息涉及到獲取會話、創建和填充消息并發送消息這些操作。您可以在獲取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塊中編寫代碼,以在創建消息并發送它時可以拋出一個異常。

練習

發送您的第一個消息

獲取消息

對于閱讀郵件來說,首先您要獲取一個會話,然后獲取并連接到一個相應的用于您的收件箱的存儲上,接著打開相應的文件夾,再獲取消息。同時,不要忘記了操作完成后關閉連接。

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.getFrom()[0]

+ "\t" + message.getSubject());

}

// Close connection

folder.close(false);

store.close();

每一條消息執行何種操作取決于自己決定。上面的代碼塊只是顯示了消息的發送者和主題。從技術上講,發送者地址列表可以為空,此時getFrom()[0]調用會拋出一個異常。

為了顯示整條消息,您可以提示用戶在看完消息的發送者和主題字段后,如果想看到消息的內容,可以再調用消息的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.getFrom()[0]

+ "\t" + message.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.writeTo(System.out);

} else if ("QUIT".equals(line)) {

break;

}

}

練習

檢查郵件

刪除消息和標志

刪除消息涉及到操作與消息關聯的標志。對不同的狀態有不同的標志,有些標志是系統定義的,有些則是由用戶定義的。預定義的標志都是在內部類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()方法來詢問文件夾。

要刪除消息,需要為消息設置DELETE標志:

message.setFlag(Flags.Flag.DELETED, true);

第一次以READ_WRITE(讀-寫)模式打開文件夾:

folder.open(Folder.READ_WRITE);

然后,處理完了所有的消息,請關閉文件夾,并傳遞true值以擦去刪除的消息。

folder.close(true);

用戶可使用Folder類的expunge()方法來刪除消息。但是,該方法對Sun公司的POP3提供程序不起作用。其他提供程序或許能也或許不能實現其功能。它更有可能適用于IMAP提供程序。由于POP只支持對收件箱的簡單訪問,使用Sun公司的提供程序時,您將不得不關閉文件夾以刪除消息。

要移去標志,只需傳遞一個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對象。下面是這種實現的一個例子,其中一個字段同時適用于兩部分內容。它不是一個Project 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()方法,以用正確的接收者和主題(添加"Re::",如果沒有的話)配置一條新消息。該方法不會為消息添加任何內容,只是為新的接收者復制發送者或回復到的報頭。該方法使用一個布爾型參數,提示是否只回復給發送者(false)或回復給所有人(true)。

MimeMessage reply = (MimeMessage)message.reply(false);

reply.setFrom(new InternetAddress("president@whitehouse.gov"));

reply.setText("Thanks");

Transport.send(reply);

在發送消息時要配置回復到地址,可使用setReplyTo()方法。

練習

回復郵件

轉發消息

轉發消息涉及的內容要稍微多一點,沒有一個專門用于轉發消息的方法,您可以通過處理組成消息的各個部分來創建要轉發的消息。

一條郵件消息可由多個部分組成,每一部分是一個BodyPart(報文部分),或更特殊一點,在操作MIME消息時則是MimeBodyPart。不同的報文部分組合到一個稱為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把資源附加到郵件消息上,并在您接收消息時獲取附件。

發送附件

發送附件與轉發消息非常相似,您要創建組成完整消息的各個部分。在創建好第一個部分即消息文本之后,您添加的用DataHandler類處理的其他部分就是您的附件,而不是轉發消息中的共享處理程序。當您從一個文件讀取附件時,附件的數據資源是FileDataSource;從URL讀取時,則是URLDataSource。一旦您有了自己的DataSource,在將其通過setDataHandler()方法最終附加到BodyPart上之前,只需將其傳遞給DataHandler類的構造器即可。假定您想保留附件的原始文件名,要做的最后一件事就是用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 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.getDisposition()方法標記上Part.ATTACHMENT配置的部分顯然就是附件。同時,附件也可以不帶有配置(和非文本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()方法只用于根據文件名創建一個文件,從輸入流中讀取字節,并將它們寫入一個文件中去。如果文件已存在,將在文件名后添加一個編號,直到找到一個不存在的文件為止。

// from saveFile()

File file = new File(filename);

for (int i=0; file.exists(); i++) {

file = new File(filename+i);

}

上面的代碼介紹了消息的各個部分被標上相應的標志的一個最簡單的例子。要想包含所有的情況,還要對disposition值為null及消息部分為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文件作為消息,并讓郵件閱讀者憂心于取出任何嵌入的圖片或相關片段,那么就可以使用消息的setContent()方法,以字符串形式傳遞消息內容,并把內容類型設置為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或某些第3方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消息中嵌入了作為消息一部分的圖片,并且您想保持消息內容的完整,就必須把圖片看作附件,并用特殊的通信標識符URL引用該圖片,該通信標識符引用的是圖片附件的內容ID報文。

嵌入圖片的處理與附加一個文件到消息上非常相似,惟一的不同之處在于:您必須區分MimeMultipart中,哪些部分是在構造器(或通過setSubType()方法)通過設置其子類型而使之相關的,以及將圖片的內容ID報頭設置成任意字符串,它將在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);

練習

發送帶有圖片的 HTML 消息

用SearchTerm搜索

JavaMail API包含一種可用于創建SearchTerm(搜索條件)的篩選機制,它可以在javax.mail.search包中找到。一旦創建了SearchTerm,您就可以詢問某個文件夾匹配的消息,并檢索出消息對象數組:

SearchTerm st = ...;

Message[] msgs = folder.search(st);

有22種不同的類可用于幫助創建搜索條件。

· AND條件(AndTerm類)

· OR條件(OrTerm類)

· NOT條件(NotTerm類)

· SENT DATE條件(SentDateTerm類)

· CONTENT條件(BodyTerm類)

· HEADER條件(FromTerm / FromStringTerm, RecipientTerm / RecipientStringTerm, SubjectTerm, etc.)

本質上,您可以為匹配的消息創建一個邏輯表達式,然后進行搜索。例如,下面顯示了一條消息的條件搜索示例,該消息帶有(部分帶有)一個ADV主題字符串,其發送者字段為friend@public.com。您可能考慮定期運行該查詢,并自動刪除任何返回的消息。

SearchTerm st =

new OrTerm(

new SubjectTerm("ADV:"),

new FromStringTerm("friend@public.com"));

Message[] msgs = folder.search(st);

[ Last edited by GShadow on 2004-11-16 at 22:58 ]

不知道自己每天都在干什么

不知道自己的未來

不知道活著是為了什么

除了不知道還是不知道

http://gshadow.5dblog.com

mail:13959560305@fj172.com

 2004-11-16 17:11                    

大嘴

積分 49

發貼 49

注冊 2004-11-21

來自 北方

狀態 離線  我想學

我想學,難嗎?

 2004-11-30 19:31                

黃新

積分 293

發貼 263

注冊 2003-6-22

來自 福建漳州

狀態 離線  友情支持一下 -貼一個我很久以前寫的支持多附件的mail發送類

package mail;

import javax.mail.*;

import javax.mail.internet.*;

import java.util.*;

import javax.activation.*;

import java.io.*;

/**

* <p>Title: </p>

* <p>Description: </p>

* <p>Copyright: Copyright (c) 2004</p>

* <p>Company: </p>

* @author 黃新

* @version 1.0

*/

public class Mail {

  public Mail() {

  }

  /**

   * 發送郵件

   * @param to 收件人

   * @param subject 主題

   * @param body 主體 郵件內容

   * @param from 法件人

   * @param PassWord 密碼

   * @param str_files 附件 數組

   * @return 是否發送成功 只能以是否跑出異常來做判斷 尋求更好的判斷方法

   */

  public boolean sendMail(String to,String subject,String body, String from,String PassWord,String []str_files){

    Properties props = new Properties();

    boolean isSended = true;

    try {

      String protocol = "smtp";//smtp  pop3

      String host = getHost(from,protocol);

      String username = getUsername(from);

      //Setup mail server

      props.put("mail."+protocol+".host",host);//smtp  pop3

      //Get Session

      props.put("mail."+protocol+".auth","true");

      PopupAuthenticator popAuthenticator = new PopupAuthenticator();

      PasswordAuthentication pop = popAuthenticator.performCheck(username,PassWord);

      Session session = Session.getInstance(props,popAuthenticator);

      //Define message

      MimeMessage message=new MimeMessage(session);

      message.setFrom(new InternetAddress(from));

      message.setSentDate(new java.util.Date ()) ;

      message.addRecipient(Message.RecipientType.TO,

                           new InternetAddress(to));

      message.setSubject(subject);

      //給消息對象設置內容

      BodyPart textBodyPart=new MimeBodyPart();

      textBodyPart.setContent(body,"text/html;charset=gb2312") ;

      Multipart mm=new MimeMultipart();

      mm.addBodyPart(textBodyPart);

      int i = str_files.length ;

        mm.addBodyPart(getFileBodyPart(str_files[i-1])) ;

        i--;

      }

      message.setContent(mm) ;

      message.saveChanges() ;

      Transport.send(message);

    }

    catch(AuthenticationFailedException afe){

      isSended = false;

      System.out.println("登陸驗證出現錯誤 ,請檢查你的用戶名和密碼") ;

      afe.printStackTrace() ;

    }

    catch(MessagingException me){

      isSended = false;

      me.printStackTrace() ;

      System.out.print("") ;

    }

    catch(Exception e){

      isSended = false;

      e.printStackTrace() ;

    }

    return isSended;

  }

  /**

   * 獲得通過郵件地址獲取用戶名

   * @param email 郵件地址

   * @return 用戶名

   */

  private String getUsername(String email){

    StringBuffer str = new StringBuffer(email);

    String usename = str.substring(0,str.indexOf("@") ) ;

    return usename;

  }

  /**

   * 通過郵件地址獲取郵件服務器地址

   * @param email 郵件地址

   * @param protocol 郵件協議

   * @return 郵件服務器主機地址

   */

  private String getHost(String email,String protocol){

    StringBuffer str = new StringBuffer(email);

    String host = protocol+"." + str.substring(str.indexOf("@")+1 ) ;

    return host;

  }

  private BodyPart getFileBodyPart(String str_file){

    BodyPart  fileBodyPart = new MimeBodyPart();

//    int NO_files = str_files.length ;

    try {

      File file = new File(str_file);

      FileDataSource fds=new FileDataSource(file);//用本地文件

      DataHandler dh = new DataHandler(fds);

      fileBodyPart.setFileName(file.getName());//可以和原文件名不一致

      fileBodyPart.setDataHandler(dh);

    }

    catch (MessagingException ME) {

      ME.printStackTrace() ;

      System.out.println("獲取文件名setFileName或設置setDataHandler時出現錯誤") ;

      return null;

    }

    return fileBodyPart;

  }

  public static void main(String[] args) {

    String[] str_files ={"G:\\192.168.7.144備份\\學習資料\\技術文章\\eclipse 參考手冊\\CSDN_eclipse 參考手冊(三).htm ","G:\\日立.html"};

    Mail m = new Mail();

    System.out.println(m.sendMail("xini_huang@hotmail.com","nihao","helloworld!!!!","huangxinyi2003@163.com","差點把自己的密碼給泄漏了",str_files)) ;

    //System.out.println(m.getUsername("huangxinyi2003@163.com")+"======"+m.getHost("huangxinyi2003@163.com")  ) ;

  }

}

[ Last edited by 黃新 on 2004-12-2 at 16:31 ]

 2004-12-2 16:26                

黃新

積分 293

發貼 263

注冊 2003-6-22

來自 福建漳州

狀態 離線  驗證

package mail;

import javax.mail.*;

import javax.mail.internet.*;

/**

* <p>Title: </p>

* <p>Description: </p>

* <p>Copyright: Copyright (c) 2004</p>

* <p>Company: </p>

* @author 黃新

* @version 1.0

*/

public class PopupAuthenticator extends Authenticator{

  String username=null;

  String password=null;

  public PopupAuthenticator(){

  }

  public PasswordAuthentication performCheck(String user,String pass){

    username = user;

    password = pass;

    return getPasswordAuthentication();

  }

  protected PasswordAuthentication getPasswordAuthentication() {

    return new PasswordAuthentication(username, password);

  }

  public static void main(String[] args) {

  }

}