1. 程式人生 > >JavaMail傳送和接收郵件

JavaMail傳送和接收郵件

一、JavaMail概述:

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

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

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

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

l         IMAP:網際網路訊息協議,是POP3的替代協議。

這三種協議都有對應SSL加密傳輸的協議,分別是SMTPS,POP3S和IMAPS。

除JavaMail服務提供程式之外,JavaMail還需要JAF(JavaBeans Activation Framework)來處理不是純文字的郵件內容,這包括MIME(多用途網際網路郵件擴充套件)、URL頁面和檔案附件等內容。下圖描述了JavaMail的體系結構。

mail.jar:此JAR檔案包含JavaMail API和Sun提供的SMTP、IMAP和POP3服務提供程式;

activation.jar:此JAR檔案包含JAF API和Sun的實現。

二、對相關協議的回顧:

       1、介紹

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

    * SMTP

    * POP

    * IMAP

    * MIME

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

       2、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 中並不支援。

       3、POP

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

       4、IMAP

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

       因為有這一更高階的效能,您也許會認為所有使用者都會使用 IMAP。事實並不是這樣。要求伺服器接收新訊息,在使用者請求時傳送到使用者手中,還要在每個使用者的多個資料夾中維護訊息。這樣雖然能將訊息集中備份,但隨著使用者長期的郵件夾越來越大,到磁碟空間耗盡時,每個使用者都會受到損失。使用 POP,就能解除安裝郵件伺服器上儲存的訊息了。

       5、MIME

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

       6、NNTP及其他

因為 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

相關推薦

JavaMail傳送接收郵件(轉載)

出處: http://blog.csdn.net/zapldy/article/details/3971579 一、JavaMail概述:        JavaMail是由Sun定義的一套收發電子郵

JavaMail傳送接收郵件API

一、JavaMail概述:     JavaMail是由Sun定義的一套收發電子郵件的API,不同的廠商可以提供自己的實現類。但它並沒有包含在JDK中,而是作為JavaEE的一部分。     廠商所提供的JavaMail服務程式可以有選擇地實現某些郵件協議,常見的郵件協議包括: SMTP:簡單

JavaMail傳送接收郵件

一、JavaMail概述: JavaMail是由Sun定義的一套收發電子郵件的API,不同的廠商可以提供自己的實現類。但它並沒有包含在JDK中,而是作為JavaEE的一部分。        廠商所提供的JavaMail服務程式可以有選擇地實現某些郵件協議,常見的郵件協議包括

Python基於tkinter傳送接收郵件

一、效果演示 二、傳送郵件程式碼 import smtplib import tkinter class Window: def __init__(self,root): label1 = tkinter.Label(root,text='SMTP

郵件實現詳解(四)------JavaMail 發送(帶圖片附件)接收郵件

發送 網絡圖 發送對象 true n) com 訪問權限 sub map   好了,進入這個系列教程最主要的步驟了,前面郵件的理論知識我們都了解了,那麽這篇博客我們將用代碼完成郵件的發送。這在實際項目中應用的非常廣泛,比如註冊需要發送郵件進行賬號激活,再比如OA項目中利用郵

電子郵件傳送接收過程 一一 SMTP、POP3、IMAP

電子郵件傳送協議主要是SMTP,收件協議主要是POP3和IMAP; SMTP 的全稱是“Simple Mail Transfer Protocol”,即簡單郵件傳輸協議。它是一組用於從源地址到目的地址

基於兩個專案 之間的RabbitMQ 傳送接收,並呼叫郵件介面,傳送郵件

專案原始碼 下載 第一個專案:qucik4j 下載地址:https://github.com/ZhangHLong/quick4j 作用: 定時監控指定服務埠是否異常,發生異常,會發送MQ訊息 通知email-server服務。

郵件傳送接收原理

一、 郵件開發涉及到的一些基本概念 1.1、郵件伺服器和電子郵箱   要在Internet上提供電子郵件功能,必須有專門的電子郵件伺服器。例如現在Internet很多提供郵件服務的廠商:sina、sohu、163等等他們都有自己的郵件伺服器。   這些郵件伺服器類似

node總結之GET/POST請求的傳送接收了解下

在我們的現實場景中,我們的node伺服器都需要跟使用者的瀏覽器打交道,也就是說建立一個互動的關係。那麼,這個關係之間的通訊基本上比較熟悉的就是get/post這種方式了。咱們這刺激來簡單看下在node中,是如何接收和處理這些關係的。 由於GET請求直接被嵌入在路徑中,URL是完整的請求路徑,

zigbee 之ZStack-2.5.1a原始碼分析(三)無線資料傳送接收

前面說過SampleApp_Init和SampleApp_ProcessEvent是我們重點關注的函式,接下來分析無線傳送和接收相關的程式碼: 在SampleApp_ProcessEvent函式中: if ( events & SYS_EVENT_MSG ) {  &nbs

【Java TCP/IP Socket程式設計】----傳送接收資料----構建解析協議訊息

--------筆記來自於書籍《Java TCP/IP Socket程式設計》。 簡介 使用套接字時,通常要麼是需要同時建立通訊通道兩端的程式,要麼實現一個給定的協議進行通訊。如果知道通訊雙方都使用java實現,且擁有對協議的完全控制權,那麼就可以使用Java的內建工具如Serialiabl

【Java TCP/IP Socket程式設計】----傳送接收資料----訊息成幀與解析

目錄   簡介 成幀與解析 成幀技術案例 簡介 在程式中使用套接字向其他程式提供資訊或者使用其他程式提供的資訊,這就需要任何需要交換資訊的程式間在資訊編碼方式上達成共識(包含了資訊交換的形式和意義),稱為協議,用來實現特定的應用程式的協議叫應用程式協議。大部分應

ARM40-A5應用程式——CAN匯流排的傳送接收

ARM40-­A5應用程式——CAN匯流排的傳送和接收 版權宣告:本文為博主原創文章,允許轉載。      ARM40-A5系列板卡共有2路隔離CAN匯流排,CAN匯流排的引腳定義見《ARM40-­A5指南——CAN匯流排介面與測試》。 一、shell中CAN匯流排的接收與傳送

用C寫一個UDP傳送接收程式

1、UDP網路程式設計主要流程 UDP協議的程式設計框架,客戶端和伺服器之間的差別在於伺服器必須使用bind()函式來繫結偵聽的本地UDP埠,而客戶端則可以不進行繫結,直接傳送到伺服器地址的某個埠地址。框圖如圖1.3所示 UDP協議的伺服器端流程 伺服器流程主要分為下述6個部分,即建立套

ROS 用 roboware實現節點資訊傳送接收

在ros下實現節點程式設計,實現一個節點發送訊息,另一個節點接收。實現方式有多種,可以直接在命令視窗建立工作空間包以及節點,用catkin_make進行編譯,新增.bash路徑,然後執行rosrun  package  node_name 。這種方式對於一個ROS初學者來說容易出錯,而且很多網上的教程中出現了

MQTT Java客戶端Eclipse paho實現資料的傳送接收

MQTT(MQ Telemetry Transport)是IBM開發的一種網路應用層的協議 使用場景: 1、不可靠、網路頻寬小的網路 2、執行的裝置CPU、記憶體非常有限 特點: 1、基於釋出/訂閱模型的協議 2、他是二進位制協議,二進位制的特點就是緊湊、佔用

js傳送接收二進位制位元組流資料

傳送二進位制資料 var oReq = new XMLHttpRequest(); oReq.open("POST", url, true); oReq.onload = function (oEvent) { // Uploaded. }; var blo

python Tcp協議迴圈傳送接收

需要建立2個檔案,一個作為客戶端,一個作為服務端 檔案一 作為客戶端client,檔案二作為服務端server 檔案一 # 建立socket物件:指定傳輸協議 # AF_INET---ipv4 # SOCK_STREAM---TCP協議 import socket

UDP完成資料的傳送接收的程式碼

要實現UDP通訊需要建立一個傳送端程式和一個接收端程式,很明顯,在通訊時只有接收端程式先執行,才能避免因傳送端傳送的資料無法接收,而造成資料丟失。因此,首先需要來完成接收端程式的編寫。 UDP完成資料的傳送 /* * 傳送端  * 1,建立DatagramSocket

《商城專案06》--用ActiveMQ實現訊息的傳送接收

一, 使用場景 對商品資訊進行操作的同時, 將資料同步到solr庫, 實現該需求有以下幾種方式: 方式1: 在e3-manager-service新增商品資訊的實現類中直接寫將資料新增到solr庫; <弊端: 負責商品資訊操作的開發人員不一定對solr熟悉, 所以得分離出來寫, 這裡可