JavaMail快速入門

 摘要
  這篇文章介紹創建基於Java的email應用程序入門知識。假如你想創建你自己的email客戶端應用程序來代替 Microsoft Outlook,或者創建一個基於Web的email系統來跟Hotmail叫板,那麼你可以從這裡開始。從JavaMail的一個不同角度出發,該文給出了一個談話email客戶端應用程序。
  在JavaMail中,你可以找到API以及其實現部分,從而用它開發功能全面的email客戶端應用程序。「email客戶端應用程序」引用了Microsoft Outlook的思想;然而,你可以寫你自己的Outlook來取而代之。但是,一個email客戶端程序不一定要駐留在一個客戶端機器上。事實上,它可以是一個在遠程服務器上運行的一個servlet或者EJB,終端用戶通過Web瀏覽器可以收發他們的email。在作者自己的寵物項目中,就使用了一個語音客戶端來讀取接收進來的消息。它是作者在「Talking Java!」中介紹的想法(在後面將有更多的介紹)的提煉。
  現在開始安裝和配置Javamail軟件。
  安裝
  如果你使用的是Java2企業版(J2EE)1.3,那麼它已經自帶有JavaMail,因此不需要進行額外的安裝。但是如果你使用的是Java2標準版(J2SE)1.1.7及以後的版本,那麼如果想要你的應用程序具備收發email的能力,下載並安裝以下兩個應用程序:
  l、JavaMail
  2、JavaBeans Activation Framework
  安裝很簡單,只需解壓下載的文件,並把所包含的jar文件添加到你機器的classpath下,以下是作者機器上的classpath:
;C:AppsJavajavamail-1.2mail.jar;C:AppsJava

javamail-1.2mailapi.jar;C:AppsJavajavamail-1.2

pop3.jar;C:AppsJavajavamail-1.2smtp.jar;C:Apps

Javajaf-1.0.1activation.jar
  mailapi.jar文件包含核心API類,而pop3.jar和smtp.jar文件包含各自的email協議實現部分。(在這篇文章中我們不使用 imap.jar文件)。可以認為實現部分跟JDBC(Java數據庫連接)驅動程序相似,不過它是用於消息系統而不是用於數據庫而已。至於 mail.jar文件,它包含了上面的每一個jar文件,因此,你可以在你的classpath中只包含mail.jar和activation.jar 文件。
  Activation.jar文件允許你通過二進制數據流的形式處理MIME類型的訪問。在後面「不僅僅可以發送普通文本」這一部分會講到DataHandler類,你可以在那找到相關信息。
  至於記錄,本文的餘下部分沒有對API做全面的講解;不過你可以學著去做一做。如果你想更多的瞭解API信息,你可以查看每個下載包中的PDF文件以及Javadoc。
  一旦你已經安裝了這個軟件,那麼你需要知道你的email帳戶情況以便運行下面的這個例子。你需要知道你的ISP的SMTP服務器名和POP服務器名,你的email帳戶登錄名以及你的郵箱密碼。圖1表示的是作者在Microsoft Outlook中使用的詳細情況:
通過SMTP發送email

  第一個例子告訴你怎樣通過SMTP發送一個基本的email消息。在下面,你將找到SimpleSender類,它從命令行讀取你的消息,然後調用一個單獨的方法send(…)來發送它們:

package com.lotontech.mail;

import javax.mail.*;

import javax.mail.internet.*;

import java.util.*;

/**

* A simple email sender class.

*/

public class SimpleSender

{

 /**

  * Main method to send a message given on the command line.

 */

 public static void main(String args[])

 {

  try

  {

   String smtpServer=args[0];

   String to=args[1];

   String from=args[2];

   String subject=args[3];

   String body=args[4];

   send(smtpServer, to, from, subject, body);

  }

  catch (Exception ex)

  {

   System.out.println("Usage: java com.lotontech.mail.SimpleSender"

       +" smtpServer toAddress fromAddress subjectText bodyText");

  }

  System.exit(0);

 }


下一步,如下所示運行SimpleSender,用你自己的SMTP替換你email設置中的smtp.myISP.net:

  java com.lotontech.mail.SimpleSender smtp.myISP.net [email protected]
[email protected] "Hello" "Just to say Hello."

  如果它能正常工作起來的話,在接收端你將看到如圖2所示的內容


     圖2 從SimpleSender讀取的消息

  SimpleSender類主要由Send(…)方法完成。其代碼如下:

/**

* "send" method to send the message.

*/

public static void send(String smtpServer, String to, String from

, String subject, String body)

 {

  try

  {

   Properties props = System.getProperties();

   // -- Attaching to default Session, or we could start a new one --

   props.put("mail.smtp.host", smtpServer);

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

   // -- Create a new message --

   Message msg = new MimeMessage(session);

   // -- Set the FROM and TO fields --

   msg.setFrom(new InternetAddress(from));

   msg.setRecipients(Message.RecipientType.TO,

   InternetAddress.parse(to, false));

   // -- We could include CC recipients too --

   // if (cc != null)

   // msg.setRecipients(Message.RecipientType.CC

   // ,InternetAddress.parse(cc, false));

   // -- Set the subject and body text --

   msg.setSubject(subject);

   msg.setText(body);

   // -- Set some other header information --

   msg.setHeader("X-Mailer", "LOTONtechEmail");

   msg.setSentDate(new Date());

   // -- Send the message --

   Transport.send(msg);

   System.out.println("Message sent OK.");

  }

  catch (Exception ex)

  {

   ex.printStackTrace();

  }

 }

 }

 
  首先,請注意,你得到一個emailsession(java.mail.Session),沒有它你什麼都做不了。在這個案例中,你調用了 Sesion.getDefultInstance(…)來得到一個共享session,其它的桌面應用程序也可以使用它;你也可以通過 Session.getInstance(…)方法建立一個新的session,它對於你的應用程序來說是唯一的。然後我們能夠證明email客戶端應用程序對每個用戶來說,其使用方法都是一樣的,比如它可以是一個用servlet實現的基於Web的email系統。

  建立一個session需要設置一些屬性;如果你通過SMTP發送消息,那麼至少需要設置mail.smtp.host屬性。在API文檔中你可以找到其它的屬性。

  現在你有了一個session,創建了一個消息。在這個例子中,你就可以設置email地址信息、主題、正文了,所有這些都取自於命令行。你也可以設置一些頭信息,包括日期等,並且你還可以指定複製(CC)的收件人。

  最後,你通過javax.mail.Transport類發送消息。如果你想知道我們的emailsession,請看後面的消息構造器。


不僅僅可以發送普通文本
  javax.mail.Message(繼承javax.mail.Part接口)類中的setText(…)方法把消息內容賦給所提供的字符串,把MIME設置為text/plain。
  但是,你不僅僅可以發送普通文本,你還可以通過setDateHandler(…)方法發送其它類型的內容。在大多數情況下,你能通過採用「其它類型內容」來指定文件附件,比如Word文檔,但是有趣的是,你檢查這裡的代碼發現它發送一個Java序列化的對象:
ByteArrayOutputStream byteStream=new ByteArrayOutputStream();
ObjectOutputStream objectStream=new ObjectOutputStream(byteStream);
objectStream.writeObject(theObject);
msg.setDataHandler(new DataHandler( new ByteArrayDataSource( byteStream.toByteArray(), "lotontech/javaobject" )));
  在javax.mail.*包結構中你可能找不到DataHandler類,因為它屬於JavaBeans Activation Framework (JAF)的javax.activation包。JAF提供處理數據內容類型的機制,這種機制主要是針對Internet內容而言,也即MIME類型。
  假如你已經試驗過了以上的代碼,通過email來發送一個Java對象,你可能碰到定位ByteArrayDataSource類的問題,因為要麼是 mail.jar要麼是activation.jar未被包含在程序裡面。可以到JavaMail demo目錄下去查找一下。
  至於你一開始就感興趣的附件,你可以在DataHandler的構造器中建立一個javax.activation.FileDataSource實例來實現。當然,你不可能單獨發送一個文件;它可以作為一個文本消息的附件發送。可能你需要理解多部分消息的概念,現在,我在接收email的環境下為你介紹這個概念。


通過POP3接受email
  在前面,我介紹了由javax.mail.Message實現的javax.mail.Part接口。我現在將解釋它的消息部分,它在這個例子中很重要。我們先看圖3。

            圖3 mail.Part接口的UML圖
  圖3表示在前面例子中建立的一個Message,它既可以是一個消息,也可以是一個消息部分,因為它實現了Part接口。對於任何部分,你都能得到它的內容(任何Java對像),並且在發送的是一個簡單文本消息的情況下,內容對象可能是一個String。對於多部分消息,內容可能是類型 Multipart,由此我們可以得到單獨的正文部分,它本身就實現了Part接口
  實際上,當你看過SimpleReceiver類的代碼之後,你會發現一切都變得很明朗。我們用三部分內容來介紹SimpleReceiver類:第一部分,類的定義以及從命令行獲取連接細節信息的main() 方法;第二部分,捕獲和查看進來消息的receive()方法;第三部分,打印頭信息和每個消息內容的printMessage()方法。
  下面是第一部分:
package com.lotontech.mail;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import java.io.*;
/**
* A simple email receiver class.
*/
public class SimpleReceiver
{
 /**
 * Main method to receive messages from the mail server specified
 * as command line arguments.
 */
 public static void main(String args[])
 {
  try
  {
   String popServer=args[0];
   String popUser=args[1];
   String popPassword=args[2];
   receive(popServer, popUser, popPassword);
  }
  catch (Exception ex)
  {
   System.out.println("Usage: java com.lotontech.mail.SimpleReceiver"
       +" popServer popUser popPassword");
  }
  System.exit(0);
}
  現在我們使用命令行來運行它(記住用你的email設置替換命令行參數):
 java com.lotontech.mail.SimpleReceiver pop.myIsp.net myUserName myPassword


receive()方法從main()方法中調用,它依次打開你的POP3信箱檢查消息,每次都調用printMessage()。代碼如下:

/**

* "receive" method to fetch messages and process them.

*/

public static void receive(String popServer, String popUser

, String popPassword)

{

 Store store=null;

 Folder folder=null;

 try

 {

  // -- Get hold of the default session --

  Properties props = System.getProperties();

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

  // -- Get hold of a POP3 message store, and connect to it --

  store = session.getStore("pop3");

  store.connect(popServer, popUser, popPassword);

  // -- Try to get hold of the default folder --

  folder = store.getDefaultFolder();

  if (folder == null) throw new Exception("No default folder");

   // -- ...and its INBOX --

   folder = folder.getFolder("INBOX");

   if (folder == null) throw new Exception("No POP3 INBOX");

    // -- Open the folder for read only --

    folder.open(Folder.READ_ONLY);

    // -- Get the message wrappers and process them --

    Message[] msgs = folder.getMessages();

    for (int msgNum = 0; msgNum < msgs.length; msgNum++)

    {

     printMessage(msgs[msgNum]);

    }

  }

  catch (Exception ex)

  {

   ex.printStackTrace();

  }

  finally

  {

   // -- Close down nicely --

   try

   {

    if (folder!=null) folder.close(false);

    if (store!=null) store.close();

   }

  catch (Exception ex2) {ex2.printStackTrace();}

 }

}


請注意:你從session中得到一個POP3消息存儲封裝器,然後使用最初在命令行上鍵入的mail設置跟它連接。

  一旦連接成功,你就得到了一個默認文件夾的句柄,在這裡使用的是INBOX文件夾,它保存了進來的消息。你可以打開這個只讀的INBOX信箱,然後一個一個的讀取消息。

  另外,你可能想知道是否你能夠以寫的方式打開這個INBOX信箱。如果你想為這些消息做標記或者從服務器上刪除,你可以做得到。不過在我們的這個例子中,你只能查看消息。

  最後,在上面的代碼中,你做到了當查看完畢後關閉文件夾以及消息存儲,然後留下printMessage()方法來完成這個類的剩餘部分。

  打印消息

  在這一部分,很有必要討論前面提到的javax.mail.Part接口。

  下面的代碼讓你明白怎樣隱含地把消息轉換為它的Part接口並且把它賦給messagePart變量。對於只有一部分的消息,你現在需要打印一些信息。

  假如調用messagePart.getContent()來生成一個Multipart實例,你知道你正在處理一個多部分消息;在這種情況下,你正在通過getBodyPart(0)來得到第一個多部分消息並且打印它。

  當然你還要知道是否你已經得到了這個消息本身,還是僅僅是消息正文的第一部份。只有當內容是普通文本或者HTML時,你纔可以打印該消息,這是通過一個InputStream來完成的。

/**

* "printMessage()" method to print a message.

*/

public static void printMessage(Message message)

{

 try

 {

  // Get the header information

  String from=((InternetAddress)message.getFrom()[0]).getPersonal();

  if (from==null) from=((InternetAddress)message.getFrom()[0])

   .getAddress();

   System.out.println("FROM: "+from);

   String subject=message.getSubject();

   System.out.println("SUBJECT: "+subject);

   // -- Get the message part (i.e. the message itself) --

   Part messagePart=message;

   Object content=messagePart.getContent();

   // -- or its first body part if it is a multipart message --

   if (content instanceof Multipart)

   {

    messagePart=((Multipart)content).getBodyPart(0);

    System.out.println("[ Multipart Message ]");

   }

   // -- Get the content type --

   String contentType=messagePart.getContentType();

   // -- If the content is plain text, we can print it --

   System.out.println("CONTENT:"+contentType);

   if (contentType.startsWith("text/plain")|| contentType.startsWith("text/html"))

   {

    InputStream is = messagePart.getInputStream();

    BufferedReader reader=new BufferedReader(new InputStreamReader(is));

               String thisLine=reader.readLine();

    while (thisLine!=null)

    {

     System.out.println(thisLine);

     thisLine=reader.readLine();

    }

   }

   System.out.println("-----------------------------");

  }

  catch (Exception ex)

  {

   ex.printStackTrace();

  }

 }

}

  為了簡單起見,我假設消息本身或者消息正文的第一部份是可以打印的。對於真正的應用軟件,可能你想要依次檢查消息正文的每一部分,並且對每一部分採取相應的行動-打印或者是保存到磁盤,這取決於內容的類型。

  當你從消息存儲中得到每個消息時,你實際上已經得到了一個輕量級的封裝器。數據內容的獲取是每申請一次就讀取一次-這對於你只想下載消息頭時很有用。


SimpleReceiver測試
  讓我們對SimpleReceiver做一次測試。為了讓它有東西可以接收,我發送圖4所示的消息(注意:消息由文本和一個附件組成)
    圖4 用於SimpleReceiver的測試消息
  一旦接收到消息,就把該消息認為是一個多部分消息。打印的文本如下:
FROM: Tony Loton
SUBJECT: Number 1
[ Multipart Message ]
CONTENT:text/plain;
charset="iso-8859-1"
Attachment 1
from Tony Loton.
-----------------------------
  把你的消息送出去
  為了有趣一點,並且說明JavaMail APIs的一個新穎的用法,我現在簡要介紹一下我的談話email項目。在做這個試驗之前你需要得到lotontalk.jar文件,並把它加到你的classpath中去,添加方法如下:
  set CLASSPATH=%CLASSPATH%;lotontalk.jar
  你也需要在SimpleReceiver類中兩個地方做代碼修改。首先在receive()方法裡面,把以下代碼:
// -- Get the message wrappers and process them --
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
  printMessage(msgs[msgNum]);

}

  替換為:

// -- Get the message wrappers and process them --

Message[] msgs = folder.getMessages();

for (int msgNum = 0; msgNum < msgs.length; msgNum++)

{

 printMessage(msgs[msgNum]);

 speakMessage(msgs[msgNum]);

}

  現在增加以下的新方法speakMessage(),它與最初的printMessage()方法相似。

/**

* "speakMessage", a talking version of printMessage().

*/

public static void speakMessage(Message message)

{

 String speech="";

 try

 {

  com.lotontech.talk.LOTONtalk speaker=new com.lotontech.talk.LOTONtalk();

  String from=((InternetAddress)message.getFrom()[0]).getPersonal();

  if (from==null) from=((InternetAddress)message.getFrom()[0]).getAddress();

   speech=speech+"from "+from+", ";

   String subject=message.getSubject();

   speech=speech+"subject "+subject+", ";

   // -- Get the message part (i.e., the message itself) --

   Part messagePart=message;

   Object content=messagePart.getContent();

   // -- ...or its first body part if it is a multipart message --

   if (content instanceof Multipart)

    messagePart=((Multipart)content).getBodyPart(0);

    String contentType=messagePart.getContentType();

    if (contentType.startsWith("text/plain")|| contentType.startsWith("text/html"))

    {

     InputStream is = messagePart.getInputStream();

     BufferedReader reader=new BufferedReader(new InputStreamReader(is));

     String thisLine=reader.readLine();

     while (thisLine!=null)

     {

      speech=speech+thisLine+". ";

      thisLine=reader.readLine();

     }

    // -- SPEAK --

    speaker.speak(speech,true);

   }

  }

  catch (Exception ex)

  {

   ex.printStackTrace();

  }

 }

  因為在說話之前,你正在把整個消息積累到一個字符串中,所以這個方案可能只適合小的消息。作為一種選擇,你可以讀一行然後再講一行。

  當然,我不可能把結果顯示給你看,因此你必須親自來做實驗。

  你還可以做一些小的試驗,當然不是在這個試驗中,來發現語音合成的一些有趣的特徵:怎樣處理數字,以及怎樣把全部大些的單詞假想成只取首字母的縮寫詞,然後一個一個字母地把它們拼出來。

  結論

  我們已經通過對發送和接收email消息的應用程序的各個基本構造塊分別講解,涉及到了收發email的方方面面。如果你是第一次接觸JavaMail,是不是發現在應用程序中收發email不是一件困難的事情。
  
發佈了25 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章