摘要: JavaMail?API是讀取、撰寫、發(fā)送電子信息的可選包。我們可用它來建立如Eudora、Foxmail、MS?Outlook?Express一般的郵件用戶代理程序(Mail?User?Agent,簡稱MUA)。讓我們看看JavaMail?API是如何提供信息訪問功能的吧!JavaMail?API被設(shè)計(jì)用于以不依賴協(xié)議的方式去發(fā)送和接收電子信息,文中著重:如何以不依賴于協(xié)議的方式發(fā)送接收電子信息,這也是本文所要描述的.?文章工具 收藏 投票評分 發(fā)表評論 復(fù)制鏈接 版權(quán)聲明:本文可以自由轉(zhuǎn)載,轉(zhuǎn)載時(shí)請務(wù)必以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明 作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig) 原文:http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html 關(guān)鍵字:java,mail,pop,smtp
一、JavaMail?API簡介 JavaMail?API是讀取、撰寫、發(fā)送電子信息的可選包。我們可用它來建立如Eudora、Foxmail、MS?Outlook?Express一般的郵件用戶代理程序(Mail?User?Agent,簡稱MUA)。而不是像sendmail或者其它的郵件傳輸代理(Mail?Transfer?Agent,簡稱MTA)程序那樣可以傳送、遞送、轉(zhuǎn)發(fā)郵件。從另外一個(gè)角度來看,我們這些電子郵件用戶日常用MUA程序來讀寫郵件,而MUA依賴著MTA處理郵件的遞送。 在清楚了到MUA與MTA之間的關(guān)系后,讓我們看看JavaMail?API是如何提供信息訪問功能的吧!JavaMail?API被設(shè)計(jì)用于以不依賴協(xié)議的方式去發(fā)送和接收電子信息,這個(gè)API被分為兩大部分:
基本功能:如何以不依賴于協(xié)議的方式發(fā)送接收電子信息,這也是本文所要描述的,不過在下文中,大家將看到這只是一廂情愿而已。 第二個(gè)部分則是依賴特定協(xié)議的,比如SMTP、POP、IMAP、NNTP協(xié)議。在這部分的JavaMail?API是為了和服務(wù)器通訊,并不在本文的內(nèi)容中。
二、相關(guān)協(xié)議一覽 在我們步入JavaMail?API之前,先看一下API所涉及的協(xié)議。以下便是大家日常所知、所樂于使用的4大信息傳輸協(xié)議: SMTP POP IMAP MIME 當(dāng)然,上面的4個(gè)協(xié)議,并不是全部,還有NNTP和其它一些協(xié)議可用于傳輸信息,但是由于不常用到,所以本文便不提及了。理解這4個(gè)基本的協(xié)議有助于我們更好的使用JavaMail?API。然而JavaMail?API是被設(shè)計(jì)為與協(xié)議無關(guān)的,目前我們并不能克服這些協(xié)議的束縛。確切的說,如果我們使用的功能并不被我們選擇的協(xié)議支持,那么JavaMail?API并不可能如魔術(shù)師一樣神奇的賦予我們這種能力。
1.SMTP 簡單郵件傳輸協(xié)議定義了遞送郵件的機(jī)制。在下文中,我們將使用基于Java-Mail的程序與公司或者ISP的SMTP服務(wù)器進(jìn)行通訊。這個(gè)SMTP服務(wù)器將郵件轉(zhuǎn)發(fā)到接收者的SMTP服務(wù)器,直至最后被接收者通過POP或者IMAP協(xié)議獲取。這并不需要SMTP服務(wù)器使用支持授權(quán)的郵件轉(zhuǎn)發(fā),但是卻的確要注意SMTP服務(wù)器的正確設(shè)置(SMTP服務(wù)器的設(shè)置與JavaMail?API無關(guān))。
2.POP POP是一種郵局協(xié)議,目前為第3個(gè)版本,即眾所周知的POP3。POP定義了一種用戶如何獲得郵件的機(jī)制。它規(guī)定了每個(gè)用戶使用一個(gè)單獨(dú)的郵箱。大多數(shù)人在使用POP時(shí)所熟悉的功能并非都被支持,例如查看郵箱中的新郵件數(shù)量。而這個(gè)功能是微軟的Outlook內(nèi)建的,那么就說明微軟Outlook之類的郵件客戶端軟件是通過查詢最近收到的郵件來計(jì)算新郵件的數(shù)量來實(shí)現(xiàn)前面所說的功能。因此在我們使用JavaMail?API時(shí)需要注意,當(dāng)需要獲得如前面所講的新郵件數(shù)量之類的信息時(shí),我們不得不自己進(jìn)行計(jì)算。
3.IMAP IMAP使用在接收信息的高級協(xié)議,目前版本為第4版,所以也被稱為IMAP4。需要注意的是在使用IMAP時(shí),郵件服務(wù)器必須支持該協(xié)議。從這個(gè)方面講,我們并不能完全使用IMAP來替代POP,不能期待IMAP在任何地方都被支持。假如郵件服務(wù)器支持IMAP,那么我們的郵件程序?qū)⒛軌蚓哂幸韵卤籌MAP所支持的特性:每個(gè)用戶在服務(wù)器上可具有多個(gè)目錄,這些目錄能在多個(gè)用戶之間共享。 其與POP相比高級之處顯而易見,但是在嘗試采取IMAP時(shí),我們認(rèn)識到它并不是十分完美的:由于IMAP需要從其它服務(wù)器上接收新信息,將這些信息遞送給用戶,維護(hù)每個(gè)用戶的多個(gè)目錄,這都為郵件服務(wù)器帶來了高負(fù)載。并且IMAP與POP的一個(gè)不同之處是POP用戶在接收郵件時(shí)將從郵件服務(wù)器上下載郵件,而IMAP允許用戶直接訪問郵件目錄,所以在郵件服務(wù)器進(jìn)行備份作業(yè)時(shí),由于每個(gè)長期使用此郵件系統(tǒng)的用戶所用的郵件目錄會(huì)占有很大的空間,這將直接導(dǎo)致郵件服務(wù)器上磁盤空間暴漲。
4.MIME MIME并不是用于傳送郵件的協(xié)議,它作為多用途郵件的擴(kuò)展定義了郵件內(nèi)容的格式:信息格式、附件格式等等。一些RFC標(biāo)準(zhǔn)都涉及了MIME:RFC?822,?RFC?2045,?RFC?2046,?RFC?2047,有興趣的Matrixer可以閱讀一下。而作為JavaMail?API的開發(fā)者,我們并不需關(guān)心這些格式定義,但是這些格式被用在了程序中。
5.NNTP和其它的第三方協(xié)議 正因?yàn)镴avaMail?API在設(shè)計(jì)時(shí)考慮到與第三方協(xié)議實(shí)現(xiàn)提供商之間的分離,故我們可以很容易的添加一些第三方協(xié)議。SUN維護(hù)著一個(gè)第三方協(xié)議實(shí)現(xiàn)提供商的列表:http://java.sun.com/products/javamail/Third_Party.html,通過此列表我們可以找到所需要的而又不被SUN提供支持的第三方協(xié)議:比如NNTP這個(gè)新聞組協(xié)議和S/MIME這個(gè)安全的MIME協(xié)議。
三、安裝 1.安裝JavaMail 為了使用JavaMail?API,需要從http://java.sun.com/products/javamail/downloads/index.html下載文件名格式為javamail-[version].zip的文件(這個(gè)文件中包括了JavaMail實(shí)現(xiàn)),并將其中的mail.jar文件添加到CLASSPATH中。這個(gè)實(shí)現(xiàn)提供了對SMTP、IMAP4、POP3的支持。 注意:在安裝JavaMail實(shí)現(xiàn)之后,我們將在demo目錄中發(fā)現(xiàn)許多有趣的簡單實(shí)例程序。 在安裝了JavaMail之后,我們還需要安裝JavaBeans?Activation?Framework,因?yàn)檫@個(gè)框架是JavaMail?API所需要的。如果我們使用J2EE的話,那么我們并無需單獨(dú)下載JavaMail,因?yàn)樗嬖谟贘2EE.jar中,只需將J2EE.jar加入到CLASSPATH即可。
2.安裝JavaBeans?Activation?Framework 從http://java.sun.com/products/javabeans/glasgow/jaf.html下載JavaBeans?Activation?Framework,并將其添加到CLASSPATH中。此框架增加了對任何數(shù)據(jù)塊的分類、以及對它們的處理的特性。這些特性是JavaMail?API需要的。雖然聽起來這些特性非常模糊,但是它對于我們的JavaMail?API來說只是提供了基本的MIME類型支持。 到此為止,我們應(yīng)當(dāng)把mail.jar和activation.jar都添加到了CLASSPATH中。 當(dāng)然如果從方便的角度講,直接把這兩個(gè)Jar文件復(fù)制到JRE目錄的lib/ext目錄中也可以。
四、初次認(rèn)識JavaMail?API 1.了解我們的JavaMail環(huán)境 A.縱覽JavaMail核心類結(jié)構(gòu) 打開JavaMail.jar文件,我們將發(fā)現(xiàn)在javax.mail的包下面存在著一些核心類:Session、Message、Address、Authenticator、Transport、Store、Folder。而且在javax.mail.internet包中還有一些常用的子類。 B.Session Session類定義了基本的郵件會(huì)話。就像Http會(huì)話那樣,我們進(jìn)行收發(fā)郵件的工作都是基于這個(gè)會(huì)話的。Session對象利用了java.util.Properties對象獲得了郵件服務(wù)器、用戶名、密碼信息和整個(gè)應(yīng)用程序都要使用到的共享信息。 Session類的構(gòu)造方法是私有的,所以我們可以使用Session類提供的getDefaultInstance()這個(gè)靜態(tài)工廠方法獲得一個(gè)默認(rèn)的Session對象: Properties?props?=?new?Properties();//?fill?props?with?any?informationSession?session?=?Session.getDefaultInstance(props,?null); 或者使用getInstance()這個(gè)靜態(tài)工廠方法獲得自定義的Session:? Properties?props?=?new?Properties();//?fill?props?with?any?informationSession?session?=?Session.getInstance(props,?null); 從上面的兩個(gè)例子中不難發(fā)現(xiàn),getDefaultInstance()和getInstance()方法的第二個(gè)參數(shù)都是null,這是因?yàn)樵谏厦娴睦又胁]有使用到郵件授權(quán),下文中將對授權(quán)進(jìn)行詳細(xì)介紹。 從很多的實(shí)例看,在對mail?server進(jìn)行訪問的過程中使用共享的Session是足夠的,即使是工作在多個(gè)用戶郵箱的模式下也不例外。
C.Message 當(dāng)我們建立了Session對象后,便可以被發(fā)送的構(gòu)造信息體了。在這里SUN提供了Message類型來幫助開發(fā)者完成這項(xiàng)工作。由于Message是一個(gè)抽象類,大多數(shù)情況下,我們使用javax.mail.internet.MimeMessage這個(gè)子類,該類是使用MIME類型、MIME信息頭的郵箱信息。信息頭只能使用US-ASCII字符,而非ASCII字符將通過編碼轉(zhuǎn)換為ASCII的方式使用。 為了建立一個(gè)MimeMessage對象,我們必須將Session對象作為MimeMessage構(gòu)造方法的參數(shù)傳入: MimeMessage?message?=?new?MimeMessage(session); 注意:對于MimeMessage類來講存在著多種構(gòu)造方法,比如使用輸入流作為參數(shù)的構(gòu)造方法。
在建立了MimeMessage對象后,我們需要設(shè)置它的各個(gè)part,對于MimeMessage類來說,這些part就是MimePart接口。最基本的設(shè)置信息內(nèi)容的方法就是通過表示信息內(nèi)容和米么類型的參數(shù)調(diào)用setContent()方法: message.setContent("Hello",?"text/plain"); 然而,如果我們所使用的MimeMessage中信息內(nèi)容是文本的話,我們便可以直接使用setText()方法來方便的設(shè)置文本內(nèi)容。 message.setText("Hello"); 前面所講的兩種方法,對于文本信息,后者更為合適。而對于其它的一些信息類型,比如HTML信息,則要使用前者。 別忘記了,使用setSubject()方法對郵件設(shè)置郵件主題: message.setSubject("First");
D.Address 到這里,我們已經(jīng)建立了Session和Message,下面將介紹如何使用郵件地址類:Address。像Message一樣,Address類也是一個(gè)抽象類,所以我們將使用javax.mail.internet.InternetAddress這個(gè)子類。 通過傳入代表郵件地址的字符串,我們可以建立一個(gè)郵件地址類: Address?address?=?new?InternetAddress("president@whitehouse.gov");? 如果要在郵件地址后面增加名字的話,可以通過傳遞兩個(gè)參數(shù):代表郵件地址和名字的字符串來建立一個(gè)具有郵件地址和名字的郵件地址類: Address?address?=?new?InternetAddress("president@whitehouse.gov",?"George?Bush");? 本文在這里所講的郵件地址類是為了設(shè)置郵件信息的發(fā)信人和收信人而準(zhǔn)備的,在建立了郵件地址類后,我們通過message的setFrom()和setReplyTo()兩種方法設(shè)置郵件的發(fā)信人: message.setFrom(address);message.setReplyTo(address); 若在郵件中存在多個(gè)發(fā)信人地址,我們可用addForm()方法增加發(fā)信人: Address?address[]?=?...;message.addFrom(address); 為了設(shè)置收信人,我們使用addRecipient()方法增加收信人,此方法需要使用Message.RecipientType的常量來區(qū)分收信人的類型: message.addRecipient(type,?address) 下面是Message.RecipientType的三個(gè)常量: Message.RecipientType.TO Message.RecipientType.CC Message.RecipientType.BCC 因此,如果我們要發(fā)送郵件給總統(tǒng),并發(fā)用一個(gè)副本給第一夫人的話,下面的方法將被用到: 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并沒有提供檢查郵件地址有效性的機(jī)制。當(dāng)然我們可以自己完成這個(gè)功能:驗(yàn)證郵件地址的字符是否按照RFC822規(guī)定的格式書寫或者通過DNS服務(wù)器上的MX記錄驗(yàn)證等。
E.Authenticator 像java.net類那樣,JavaMail?API通過使用授權(quán)者類(Authenticator)以用戶名、密碼的方式訪問那些受到保護(hù)的資源,在這里“資源”就是指郵件服務(wù)器。在javax.mail包中可以找到這個(gè)JavaMail的授權(quán)者類(Authenticator)。 在使用Authenticator這個(gè)抽象類時(shí),我們必須采用繼承該抽象類的方式,并且該繼承類必須具有返回PasswordAuthentication對象(用于存儲認(rèn)證時(shí)要用到的用戶名、密碼)getPasswordAuthentication()方法。并且要在Session中進(jìn)行注冊,使Session能夠了解在認(rèn)證時(shí)該使用哪個(gè)類。 下面代碼片斷中的MyAuthenticator就是一個(gè)Authenticator的子類。 Properties?props?=?new?Properties();//?fill?props?with?any?informationAuthenticator?auth?=?new?MyAuthenticator();Session?session?=?Session.getDefaultInstance(props,?auth);
F.Transport 在發(fā)送信息時(shí),Transport類將被用到。這個(gè)類實(shí)現(xiàn)了發(fā)送信息的協(xié)議(通稱為SMTP),此類是一個(gè)抽象類,我們可以使用這個(gè)類的靜態(tài)方法send()來發(fā)送消息: Transport.send(message); 當(dāng)然,方法是多樣的。我們也可由Session獲得相應(yīng)協(xié)議對應(yīng)的Transport實(shí)例。并通過傳遞用戶名、密碼、郵件服務(wù)器主機(jī)名等參數(shù)建立與郵件服務(wù)器的連接,并使用sendMessage()方法將信息發(fā)送,最后關(guān)閉連接: message.saveChanges();?//?implicit?with?send()Transport?transport?=?session.getTransport("smtp");transport.connect(host,?username,?password);transport.sendMessage(message,?message.getAllRecipients());transport.close(); 評論:上面的方法是一個(gè)很好的方法,尤其是在我們在同一個(gè)郵件服務(wù)器上發(fā)送多個(gè)郵件時(shí)。因?yàn)檫@時(shí)我們將在連接郵件服務(wù)器后連續(xù)發(fā)送郵件,然后再關(guān)閉掉連接。send()這個(gè)基本的方法是在每次調(diào)用時(shí)進(jìn)行與郵件服務(wù)器的連接的,對于在同一個(gè)郵件服務(wù)器上發(fā)送多個(gè)郵件來講可謂低效的方式。 注意:如果需要在發(fā)送郵件過程中監(jiān)控mail命令的話,可以在發(fā)送前設(shè)置debug標(biāo)志: session.setDebug(true)。
G.Store和Folder 接收郵件和發(fā)送郵件很類似都要用到Session。但是在獲得Session后,我們需要從Session中獲取特定類型的Store,然后連接到Store,這里的Store代表了存儲郵件的郵件服務(wù)器。在連接Store的過程中,極有可能需要用到用戶名、密碼或者Authenticator。 //?Store?store?=?session.getStore("imap");Store?store?=?session.getStore("pop3");store.connect(host,?username,?password); 在連接到Store后,一個(gè)Folder對象即目錄對象將通過Store的getFolder()方法被返回,我們可從這個(gè)Folder中讀取郵件信息: Folder?folder?=?store.getFolder("INBOX");folder.open(Folder.READ_ONLY);Message?message[]?=?folder.getMessages(); 上面的例子首先從Store中獲得INBOX這個(gè)Folder(對于POP3協(xié)議只有一個(gè)名為INBOX的Folder有效),然后以只讀(Folder.READ_ONLY)的方式打開Folder,最后調(diào)用Folder的getMessages()方法得到目錄中所有Message的數(shù)組。
注意:對于POP3協(xié)議只有一個(gè)名為INBOX的Folder有效,而對于IMAP協(xié)議,我們可以訪問多個(gè)Folder(想想前面講的IMAP協(xié)議)。而且SUN在設(shè)計(jì)Folder的getMessages()方法時(shí)采取了很智能的方式:首先接收新郵件列表,然后再需要的時(shí)候(比如讀取郵件內(nèi)容)才從郵件服務(wù)器讀取郵件內(nèi)容。 在讀取郵件時(shí),我們可以用Message類的getContent()方法接收郵件或是writeTo()方法將郵件保存,getContent()方法只接收郵件內(nèi)容(不包含郵件頭),而writeTo()方法將包括郵件頭。 System.out.println(((MimeMessage)message).getContent()); 在讀取郵件內(nèi)容后,別忘記了關(guān)閉Folder和Store。 folder.close(aBoolean);store.close(); 傳遞給Folder.close()方法的boolean?類型參數(shù)表示是否在刪除操作郵件后更新Folder。?
H.繼續(xù)向前進(jìn)! 在講解了以上的七個(gè)Java?Mail核心類定義和理解了簡單的代碼片斷后,下文將詳細(xì)講解怎樣使用這些類實(shí)現(xiàn)JavaMail?API所要完成的高級功能。
五、使用JavaMail?API 在明確了JavaMail?API的核心部分如何工作后,本人將帶領(lǐng)大家學(xué)習(xí)一些使用Java?Mail?API任務(wù)案例。 1.發(fā)送郵件 在獲得了Session后,建立并填入郵件信息,然后發(fā)送它到郵件服務(wù)器。這便是使用Java?Mail?API發(fā)送郵件的過程,在發(fā)送郵件之前,我們需要設(shè)置SMTP服務(wù)器:通過設(shè)置Properties的mail.smtp.host屬性。 String?host?=?...;String?from?=?...;String?to?=?...;//?Get?system?propertiesProperties?props?=?System.getProperties();//?Setup?mail?serverprops.put("mail.smtp.host",?host);//?Get?sessionSession?session?=?Session.getDefaultInstance(props,?null);//?Define?messageMimeMessage?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?messageTransport.send(message); 由于建立郵件信息和發(fā)送郵件的過程中可能會(huì)拋出異常,所以我們需要將上面的代碼放入到try-catch結(jié)構(gòu)塊中。
2.接收郵件 為了在讀取郵件,我們獲得了session,并且連接到了郵箱的相應(yīng)store,打開相應(yīng)的Folder,然后得到我們想要的郵件,當(dāng)然別忘記了在結(jié)束時(shí)關(guān)閉連接。 String?host?=?...;String?username?=?...;String?password?=?...;//?Create?empty?propertiesProperties?props?=?new?Properties();//?Get?sessionSession?session?=?Session.getDefaultInstance(props,?null);//?Get?the?storeStore?store?=?session.getStore("pop3");store.connect(host,?username,?password);//?Get?folderFolder?folder?=?store.getFolder("INBOX");folder.open(Folder.READ_ONLY);//?Get?directoryMessage?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(); 上面的代碼所作的是從郵箱中讀取每個(gè)郵件,并且顯示郵件的發(fā)信人地址和主題。從技術(shù)角度講,這里存在著一個(gè)異常的可能:當(dāng)發(fā)信人地址為空時(shí),getFrom()[0]將拋出異常。
下面的代碼片斷有效的說明了如何讀取郵件內(nèi)容,在顯示每個(gè)郵件發(fā)信人和主題后,將出現(xiàn)用戶提示從而得到用戶是否讀取該郵件的確認(rèn),如果輸入YES的話,我們可用Message.writeTo(java.io.OutputStream?os)方法將郵件內(nèi)容輸出到控制臺上,關(guān)于Message.writeTo()的具體用法請看JavaMail?API。 BufferedReader?reader?=?new?BufferedReader?(??new?InputStreamReader(System.in));//?Get?directoryMessage?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;??}}
3.刪除郵件和標(biāo)志 設(shè)置與message相關(guān)的Flags是刪除郵件的常用方法。這些Flags表示了一些系統(tǒng)定義和用戶定義的不同狀態(tài)。在Flags類的內(nèi)部類Flag中預(yù)定義了一些標(biāo)志: Flags.Flag.ANSWERED Flags.Flag.DELETED Flags.Flag.DRAFT Flags.Flag.FLAGGED Flags.Flag.RECENT Flags.Flag.SEEN Flags.Flag.USER 但需要在使用時(shí)注意的:標(biāo)志存在并非意味著這個(gè)標(biāo)志被所有的郵件服務(wù)器所支持。例如,對于刪除郵件的操作,POP協(xié)議不支持上面的任何一個(gè)。所以要確定哪些標(biāo)志是被支持的??通過訪問一個(gè)已經(jīng)打開的Folder對象的getPermanetFlags()方法,它將返回當(dāng)前被支持的Flags類對象。 刪除郵件時(shí),我們可以設(shè)置郵件的DELETED標(biāo)志:? message.setFlag(Flags.Flag.DELETED,?true); 但是首先要采用READ_WRITE的方式打開Folder: folder.open(Folder.READ_WRITE); 在對郵件進(jìn)行刪除操作后關(guān)閉Folder時(shí),需要傳遞一個(gè)true作為對刪除郵件的擦除確認(rèn)。 folder.close(true); Folder類中另一種用于刪除郵件的方法expunge()也同樣可刪除郵件,但是它并不為sun提供的POP3實(shí)現(xiàn)支持,而其它第三方提供的POP3實(shí)現(xiàn)支持或者并不支持這種方法。 另外,介紹一種檢查某個(gè)標(biāo)志是否被設(shè)置的方法:Message.isSet(Flags.Flag?flag)方法,其中參數(shù)為被檢查的標(biāo)志。
4.郵件認(rèn)證 我們在前面已經(jīng)學(xué)會(huì)了如何使用Authenticator類來代替直接使用用戶名和密碼這兩字符串作為Session.getDefaultInstance()或者Session.getInstance()方法的參數(shù)。在前面的小試牛刀后,現(xiàn)在我們將了解到全面認(rèn)識一下郵件認(rèn)證。 我們在此取代了直接使用郵件服務(wù)器主機(jī)名、用戶名、密碼這三個(gè)字符串作為連接到POP3?Store的方式,使用存儲了郵件服務(wù)器主機(jī)名信息的屬性文件,并在獲得Session時(shí)傳入自定義的Authenticator實(shí)例: //?Setup?propertiesProperties?props?=?System.getProperties();props.put("mail.pop3.host",?host);//?Setup?authentication,?get?sessionAuthenticator?auth?=?new?PopupAuthenticator();Session?session?=?Session.getDefaultInstance(props,?auth);//?Get?the?storeStore?store?=?session.getStore("pop3");store.connect();
PopupAuthenticator類繼承了抽象類Authenticator,并且通過重載Authenticator類的getPasswordAuthentication()方法返回PasswordAuthentication類對象。而getPasswordAuthentication()方法的參數(shù)param是以逗號分割的用戶名、密碼組成的字符串。 import?javax.mail.*;import?java.util.*;public?class?PopupAuthenticator?extends?Authenticator?{??public?PasswordAuthentication?getPasswordAuthentication(String?param)?{????String?username,?password;????StringTokenizer?st?=?new?StringTokenizer(param,?",");????username?=?st.nextToken();????password?=?st.nextToken();????return?new?PasswordAuthentication(username,?password);??}}
5.回復(fù)郵件 回復(fù)郵件的方法很簡單:使用Message類的reply()方法,通過配置回復(fù)郵件的收件人地址和主題(如果沒有提供主題的話,系統(tǒng)將默認(rèn)將“Re:”作為郵件的主體),這里不需要設(shè)置任何的郵件內(nèi)容,只要復(fù)制發(fā)信人或者reply-to到新的收件人。而reply()方法中的boolean參數(shù)表示是否將郵件回復(fù)給發(fā)送者(參數(shù)值為false),或是恢復(fù)給所有人(參數(shù)值為true)。 補(bǔ)充一下,reply-to地址需要在發(fā)信時(shí)使用setReplyTo()方法設(shè)置。 MimeMessage?reply?=?(MimeMessage)message.reply(false);reply.setFrom(new?InternetAddress("president@whitehouse.gov"));reply.setText("Thanks");Transport.send(reply);
6.轉(zhuǎn)發(fā)郵件 轉(zhuǎn)發(fā)郵件的過程不如前面的回復(fù)郵件那樣簡單,它將建立一個(gè)轉(zhuǎn)發(fā)郵件,這并非一個(gè)方法就能做到。 每個(gè)郵件是由多個(gè)部分組成,每個(gè)部分稱為一個(gè)郵件體部分,是一個(gè)BodyPart類對象,對于MIME類型郵件來講就是MimeBodyPart類對象。這些郵件體包含在成為Multipart的容器中對于MIME類型郵件來講就是MimeMultiPart類對象。在轉(zhuǎn)發(fā)郵件時(shí),我們建立一個(gè)文字郵件體部分和一個(gè)被轉(zhuǎn)發(fā)的文字郵件體部分,然后將這兩個(gè)郵件體放到一個(gè)Multipart中。說明一下,復(fù)制一個(gè)郵件內(nèi)容到另一個(gè)郵件的方法是僅復(fù)制它的DataHandler(數(shù)據(jù)處理者)即可。這是由JavaBeans?Activation?Framework定義的一個(gè)類,它提供了對郵件內(nèi)容的操作命令的訪問、管理了郵件內(nèi)容操作,是不同的數(shù)據(jù)源和數(shù)據(jù)格式之間的一致性接口。 //?Create?the?message?to?forwardMessage?forward?=?new?MimeMessage(session);//?Fill?in?headerforward.setSubject("Fwd:?"?+?message.getSubject());forward.setFrom(new?InternetAddress(from));forward.addRecipient(Message.RecipientType.TO,???new?InternetAddress(to));//?Create?your?new?message?partBodyPart?messageBodyPart?=?new?MimeBodyPart();messageBodyPart.setText(??"Here?you?go?with?the?original?message:\n\n");//?Create?a?multi-part?to?combine?the?partsMultipart?multipart?=?new?MimeMultipart();multipart.addBodyPart(messageBodyPart);//?Create?and?fill?part?for?the?forwarded?contentmessageBodyPart?=?new?MimeBodyPart();messageBodyPart.setDataHandler(message.getDataHandler());//?Add?part?to?multi?partmultipart.addBodyPart(messageBodyPart);//?Associate?multi-part?with?messageforward.setContent(multipart);//?Send?messageTransport.send(forward);
7.使用附件 附件作為與郵件相關(guān)的資源經(jīng)常以文本、表格、圖片等格式出現(xiàn),如流行的郵件客戶端一樣,我們可以用JavaMail?API從郵件中獲取附件或是發(fā)送帶有附件的郵件。
A.發(fā)送帶有附件的郵件 發(fā)送帶有附件的郵件的過程有些類似轉(zhuǎn)發(fā)郵件,我們需要建立一個(gè)完整郵件的各個(gè)郵件體部分,在第一個(gè)部分(即我們的郵件內(nèi)容文字)后,增加一個(gè)具有DataHandler的附件而不是在轉(zhuǎn)發(fā)郵件時(shí)那樣復(fù)制第一個(gè)部分的DataHandler。
如果我們將文件作為附件發(fā)送,那么要建立FileDataSource類型的對象作為附件數(shù)據(jù)源;如果從URL讀取數(shù)據(jù)作為附件發(fā)送,那么將要建立URLDataSource類型的對象作為附件數(shù)據(jù)源。
然后將這個(gè)數(shù)據(jù)源(FileDataSource或是URLDataSource)對象作為DataHandler類構(gòu)造方法的參數(shù)傳入,從而建立一個(gè)DataHandler對象作為數(shù)據(jù)源的DataHandler。
接著將這個(gè)DataHandler設(shè)置為郵件體部分的DataHandler。這樣就完成了郵件體與附件之間的關(guān)聯(lián)工作,下面的工作就是BodyPart的setFileName()方法設(shè)置附件名為原文件名。
最后將兩個(gè)郵件體放入到Multipart中,設(shè)置郵件內(nèi)容為這個(gè)容器Multipart,發(fā)送郵件。 //?Define?messageMessage?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?messagemessageBodyPart.setText("Pardon?Ideas");Multipart?multipart?=?new?MimeMultipart();multipart.addBodyPart(messageBodyPart);//?Part?two?is?attachmentmessageBodyPart?=?new?MimeBodyPart();DataSource?source?=?new?FileDataSource(filename);messageBodyPart.setDataHandler(new?DataHandler(source));messageBodyPart.setFileName(filename);multipart.addBodyPart(messageBodyPart);//?Put?parts?in?messagemessage.setContent(multipart);//?Send?the?messageTransport.send(message); 如果我們使用servlet實(shí)現(xiàn)發(fā)送帶有附件的郵件,則必須上傳附件給servlet,這時(shí)需要注意提交頁面form中對編碼類型的設(shè)置應(yīng)為multipart/form-data。 <FORM?ENCTYPE="multipart/form-data"?????method=post?action="/myservlet">???<INPUT?TYPE="file"?NAME="thefile">??<INPUT?TYPE="submit"?VALUE="Upload"></FORM>
B.讀取郵件中的附件 讀取郵件中的附件的過程要比發(fā)送它的過程復(fù)雜一點(diǎn)。因?yàn)閹в懈郊泥]件是多部分組成的,我們必須處理每一個(gè)部分獲得郵件的內(nèi)容和附件。 但是如何辨別郵件信息內(nèi)容和附件呢?Sun在Part類(BodyPart類實(shí)現(xiàn)的接口類)中提供了getDisposition()方法讓開發(fā)者獲得郵件體部分的部署類型,當(dāng)該部分是附件時(shí),其返回之將是Part.ATTACHMENT。但附件也可以沒有部署類型的方式存在或者部署類型為Part.INLINE,無論部署類型為Part.ATTACHMENT還是Part.INLINE,我們都能把該郵件體部分導(dǎo)出保存。 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方法是自定義的方法,它根據(jù)附件的文件名建立一個(gè)文件,如果本地磁盤上存在名為附件的文件,那么將在文件名后增加數(shù)字表示區(qū)別。然后從郵件體中讀取數(shù)據(jù)寫入到本地文件中(代碼省略)。 //?from?saveFile()File?file?=?new?File(filename);for?(int?i=0;?file.exists();?i++)?{??file?=?new?File(filename+i);} 以上是郵件體部分被正確設(shè)置的簡單例子,如果郵件體部分的部署類型為null,那么我們通過獲得郵件體部分的MIME類型來判斷其類型作相應(yīng)的處理,代碼結(jié)構(gòu)框架如下: 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,?...??}...}
8.處理HTML郵件 前面的例子中發(fā)送的郵件都是以文本為內(nèi)容的(除了附件),下面將介紹如何接收和發(fā)送基于HTML的郵件。 A.發(fā)送HTML郵件 假如我們需要發(fā)送一個(gè)HTML文件作為郵件內(nèi)容,并使郵件客戶端在讀取郵件時(shí)獲取相關(guān)的圖片或者文字的話,只要設(shè)置郵件內(nèi)容為html代碼,并設(shè)置內(nèi)容類型為text/html即可: String?htmlText?=?"<H1>Hello</H1>"?+???"<img?src=\"http://www.jguru.com/images/logo.gif\">";message.setContent(htmlText,?"text/html")); 請注意:這里的圖片并不是在郵件中內(nèi)嵌的,而是在URL中定義的。郵件接收者只有在線時(shí)才能看到。 在接收郵件時(shí),如果我們使用JavaMail?API接收郵件的話是無法實(shí)現(xiàn)以HTML方式顯示郵件內(nèi)容的。因?yàn)镴avaMail?API郵件內(nèi)容視為二進(jìn)制流。所以要顯示HTML內(nèi)容的郵件,我們必須使用JEditorPane或者第三方HTML展現(xiàn)組件。
以下代碼顯示了如何使用JEditorPane顯示郵件內(nèi)容: 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();}
B.在郵件中包含圖片 如果我們在郵件中使用HTML作為內(nèi)容,那么最好將HTML中使用的圖片作為郵件的一部分,這樣無論是否在線都會(huì)正確的顯示HTML中的圖片。處理方法就是將HTML中用到的圖片作為郵件附件并使用特殊的cid?URL作為圖片的引用,這個(gè)cid就是對圖片附件的Content-ID頭的引用。 處理內(nèi)嵌圖片就像向郵件中添加附件一樣,不同之處在于我們必須通過設(shè)置圖片附件所在的郵件體部分的header中Content-ID為一個(gè)隨機(jī)字符串,并在HTML中img的src標(biāo)記中設(shè)置為該字符串。這樣就完成了圖片附件與HTML的關(guān)聯(lián)。 String?file?=?...;//?Create?the?messageMessage?message?=?new?MimeMessage(session);//?Fill?its?headersmessage.setSubject("Embedded?Image");message.setFrom(new?InternetAddress(from));message.addRecipient(Message.RecipientType.TO,???new?InternetAddress(to));//?Create?your?new?message?partBodyPart?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?partsMimeMultipart?multipart?=?new?MimeMultipart("related");multipart.addBodyPart(messageBodyPart);//?Create?part?for?the?imagemessageBodyPart?=?new?MimeBodyPart();//?Fetch?the?image?and?associate?to?partDataSource?fds?=?new?FileDataSource(file);messageBodyPart.setDataHandler(new?DataHandler(fds));messageBodyPart.setHeader("Content-ID","<memememe>");//?Add?part?to?multi-partmultipart.addBodyPart(messageBodyPart);//?Associate?multi-part?with?messagemessage.setContent(multipart);
9.在郵件中搜索短語 JavaMail?API提供了過濾器機(jī)制,它被用來建立搜索短語。這個(gè)短語由javax.mail.search包中的SearchTerm抽象類來定義,在定義后我們便可以使用Folder的Search()方法在Folder中查找郵件: SearchTerm?st?=?...;Message[]?msgs?=?folder.search(st); 下面有22個(gè)不同的類(繼承了SearchTerm類)供我們使用: AND?terms?(class?AndTerm) OR?terms?(class?OrTerm) NOT?terms?(class?NotTerm) SENT?DATE?terms?(class?SentDateTerm) CONTENT?terms?(class?BodyTerm) HEADER?terms?(FromTerm?/?FromStringTerm,?RecipientTerm?/?RecipientStringTerm,?SubjectTerm,?etc.) 使用這些類定義的斷語集合,我們可以構(gòu)造一個(gè)邏輯表達(dá)式,并在Folder中進(jìn)行搜索。下面是一個(gè)實(shí)例:在Folder中搜索郵件主題含有“ADV”字符串或者發(fā)信人地址為friend@public.com的郵件。 SearchTerm?st?=???new?OrTerm(????new?SubjectTerm("ADV:"),?????new?FromStringTerm("friend@public.com"));Message[]?msgs?=?folder.search(st);
六、參考資源 JavaMail?API?Home Sun’s?JavaMail?API基礎(chǔ) JavaBeans?Activation?Framework?Home javamail-interest?mailing?list Sun's?JavaMail?FAQ jGuru's?JavaMail?FAQ Third?Party?Products?List 七、代碼下載 http://java.sun.com/developer/onlineTraining/JavaMail/exercises.html? |
|