JavaMail使用教程

阿将邮件工具结合到您独立于平台的 Java 解决方案中吗?再没有比 JavaMail API 更合适的了,它提供了个独立于协议的模块,能用于 IMAPPOPSMTPMIME 和其它涉及因特网的消息传递协议。现在,有了 JavaBeans Activation Framework (JAF)的支持,您的应用程序经由 JavaMail API 就能启用邮件功能啦。

概念

学完这个模块后,您将了解:

· 因特网邮件协议 SMTPPOP3IMAP MIME 的基本知识

· JavaMail 框架的体系结构

· JavaMail API JavaBeans Activation Framework 之间的连接

目的

学完这个模块后,您将能够:

· 使用 JavaMail API 发送和阅读邮件

· 处理发送和接受邮件附件

· 处理 HTML 文件格式的消息

· 使用搜索条件搜索消息

先决条件

课程含有如何下载及安装 JavaMail API 的指导。此外,您还需一个开发环境,如 JDK 1.1.6+ Java 2 平台标准版(Java 2 Platform, Standard Edition J2EE1.2.x 1.3.x

有必要对面向对象编程概念和 Java 编程语言大致熟悉。教程 Java 语言要点会有所帮助。

JavaMail API 是什么呢?

JavaMail API 是一个用于阅读、编写和发送电子消息的可选包(标准扩展)。与 Eudorapine Microsoft Outlook 相似,这个包用来创建邮件用户代理(Mail User AgentMUA类型程序。API 的主要用途并不在于传输、发送和转发消息;这一功能范围属于某些应用程序,如 sendmail 及其它邮件传输代理(Mail Transfer AgentMTA)类型程序。MUA 类型的程序能让用户阅读和书写邮件,而它却依赖 MTA 处理实际发送。

JavaMail API 打算将 API 分成两部分来支持独立于协议的发送和接受消息:

· API 的第一部分是这个课程的焦点基本上来说,是如何发送和接受独立于供应商/协议消息。

· 第二部分谈到特定协议的语言, SMTPPOPIMAP NNTP。有了 JavaMail API,要与服务器通信,还需要每个协议的供应商(provider。此课程不涉及创建特定的协议供应商,因为 Sun 已经为我们免费提供了很充分的一组。

在研究 JavaMail API 的细则之前,让我们回顾用于 API 的协议。基本上,您会逐渐熟悉并喜爱的协议有四个:

· SMTP

· POP

· IMAP

· MIME

您还将碰到 NNTP 和其它协议。理解所有协议的基本知识将有助于您理解如何使用 JavaMail API。虽然不了解这些协议您照样可以用这个 API,却不能够克服那些基础协议的局限性。如果我们精选的协议不能支持某种性能,JavaMail API 决不能魔术般的将这种性能添加上去。(您很快就会看到,在处理 POP 时这将成为一个难题。)

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 中并不支持。

POP: 代表邮局协议(Post Office Protocol)。目前用的是版本 3,也称 POP3RFC 1939 定义了这个协议。POP 是一种机制,因特网上大多数人用它得到邮件。它规定每个用户一个邮箱的支持。这就是它所能做的,而这也造成了许多混淆。使用 POP 时,用户熟悉的许多性能并不是由 POP 协议支持的,如查看有几封新邮件消息这一性能。这些性能内建于如 Eudora Microsoft Outlook 之类的程序中,它们能记住一些事,诸如最近一次收到的邮件,还能计算出有多少是新的。所以当使用 JavaMail API 时,如果您想要这类信息,您就必须自己算。

IMAP 是更高级的用于接收消息的协议。在 RFC 2060 中被定义,IMAP 代表因特网消息访问协议(Internet Message Access Protocol),目前用的是版本 4,也称 IMAP4。在用到 IMAP 时,邮件服务器必需支持这个协议。不能仅仅把使用 POP 的程序用于 IMAP,并指望它支持 IMAP 所有性能。假设邮件服务器支持 IMAP,基于 JavaMail 的程序可以利用这种情况用户在服务器上有多个文件夹(folder),并且这些文件夹可以被多个用户共享。

因为有这一更高级的性能,您也许会认为所有用户都会使用 IMAP。事实并不是这样。要求服务器接收新消息,在用户请求时发送到用户手中,还要在每个用户的多个文件夹中维护消息。这样虽然能将消息集中备份,但随着用户长期的邮件夹越来越大,到磁盘空间耗尽时,每个用户都会受到损失。使用 POP,就能卸载邮件服务器上保存的消息了。

MIME 代表多用途因特网邮件扩展标准(Multipurpose Internet Mail Extensions)。它不是邮件传输协议。但对传输内容的消息、附件及其它的内容定义了格式。这里有很多不同的有效文档:RFC 822RFC 2045RFC 2046 RFC 2047。作为一个 JavaMail API 的用户,您通常不必对这些格式操心。无论如何,一定存在这些格式而且程序会用到它。

因为 JavaMail API 将供应商和所有其它的东西分开了,您就能轻松添加额外的协议支持。Sun 保留了一张第三方供应商列表,他们利用了 Sun 不提供超出(out-of-the-box)支持范围的协议。您会找到 NNTP(网络新闻传输协议)[新闻组]S/MIME(安全多用途因特网邮件扩展)及其它支持。

安装JavaMail:

现在有两种常用的 JavaMail API 版本:1.2 1.1.3。这一课程的所有示例都能在这两种版本上正常运行。虽然版本 1.2 是最新版,但版本 1.1.3 包含了 Java 2 平台企业版(Java 2 Platform, Enterprise EditionJ2EE)的版本 1.2.1,所以仍然有很多人使用它。您要用的 JavaMail API 的版本取决于您下载和安装的东西,所有这些将在JDK 1.1.6+J2EE 版本 1.2.x J2SE 版本 1.3.x 下工作。

注意:安装完 Sun JavaMail 实现后,您可以在demo目录中找到很多示例程序。

要使用 JavaMail 1.2 API,请下载 JavaMail 1.2 实现,解开javamail-1_2.zip文件,并将mail.jar文件添加到您的 CLASSPATH 中。除了核心类,随版本 1.2 实现一起提供的还有 SMTPIMAP4 POP3 供应商。

安装完 JavaMail 1.2 之后,安装 JavaBeans Activation Framework

JavaMail API 的所有版本都需要 JavaBeans Activation Framework 来支持任意数据块的输入及相应处理。功能似乎不多,但目前许多浏览器和邮件工具中都能找到这种基本的 MIME 型支持。下载完框架后,解开jaf1_0_1.zip文件,并将activation.jar文件添加到 CLASSPATH 中。

对于 JavaMail 1.2 用户来说,现在您应该已将mail.jaractivation.jar文件添加到 CLASSPATH 中了。

而对于 JavaMail 1.1.3 用户,现在您应该已将mail.jarpop3.jaractivation.jar文件添加到 CLASSPATH 中了。如果不打算用 POP3,就不必将pop3.jar添加到 CLASSPATH 中去。

如果您不想更改 CLASSPATH 环境变量,将 jar 文件复制到您 Java 运行时环境(Java Runtime EnvironmentJRE)目录下的lib/ext目录中去。例如,J2SE 1.3 发行版的缺省目录在 Windows 平台的C:/jdk1.3/jre/lib/ext

如果您使用 J2EE,就没有什么特定的事非要用基本 JavaMail API 来做不可;J2EE 的类就能处理了。只要确保j2ee.jar文件已在您的CLASSPATH 中并已全部设置好。

练习 1. 如何设置 JavaMail 环境

在这个练习中,您将安装 Sun JavaMail 相关工具。安装结束后,将介绍符合参考实现的演示程序。

任务 1请下载 Sun JavaMail API 实现 的最新版本。

任务 2请下载 Sun JavaBeans Activation Framework 的最新版本。

任务 3解压缩下载的软件包。您就得到能支持所有平台的两个包的 ZIP 文件。

任务 3 的帮助:你可以使用jar工具将包解压缩。

任务 4 JavaMail 1.2 下载软件包中的mail.jar文件以及 JavaBeans Activation Framework 下载软件包中的activation.jar文件添加到您的CLASSPATH中。

任务 4 的帮助:将文件复制到您的扩展库目录下面。Microsoft Windows 的用户使用缺省的安装复制,可输入如下命令:

cd /javamail-1.2

copy mail.jar /jdk1.3/jre/lib/ext

cd /jaf-1.0.1

copy activation.jar /jdk1.3/jre/lib/ext

如果您不愿意将文件复制到扩展库目录,Sun 提供在 Windows NT 上设置 CLASSPATH 详细指导

任务 5进入 JavaMail API 工具的demo目录,编译msgsend程序以发送一条测试消息。

任务 5 的帮助:

javac msgsend.java

任务 6执行程序,用-o选项传入from地址、用-M选项传入 SMTP 服务器,还有传入to地址(没有选项)。然后,您输入主题,消息正文和文件结束符(CTRL-Z)标志消息输入结束。

任务 6 的帮助:务必替换from地址、SMTP 服务器和to地址。

java msgsend -o from@address -M SMTP.Server to@address

如果您不能确定您的 SMTP 服务器,请联系您的系统管理员或与您的互联网服务供应商核对。

任务 7
用您的标准邮件阅读程序(EudoraOutlook Expresspine...)检查,确保消息已收到。

JavaMail核心类概览:

在采用深入探索 JavaMail 类的入门方法前,这一章向您展示构成 API 的核心类:SessionMessageAddressAuthenticatorTransportStoreFolder。尽管您常会发现自己使用的子类在javax.mail.internet中,但上述这些类却放在 JavaMail API的顶级包javax.mail里。

Session

Session类定义了一个基本邮件会话(session)。所有其它类都是经由这个session 才得以生效。Session对象用java.util.Properties对象获取信息,如邮件服务器、用户名、密码及整个应用程序中共享的其它信息。

类的构造器是私有的(private)。您可以得到单个缺省 session,它能用getDefaultInstance()方法被共享:

Properties props = new Properties();

// fill props with any information

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

或者,您还可以用getInstance()创建一个独立的 session

Properties props = new Properties();

// fill props with any information

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

对于这两种情况,null参数都是Authenticator对象(在这次没有使用)。

对于大多数情况,共享的 session 已经够用了,即使要处理多个用户邮箱的邮件 session 也一样。您可以在通信过程中稍后的步骤加入用户名和密码组合,让一切保持独立。

Message

一旦获得Session对象,就可以继续创建要发送的消息。这由Message类来完成。因为Message是个抽象类,您必需用一个子类,多数情况下为javax.mail.internet.MimeMessageMimeMessage是个能理解 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");

Address

一旦您创建了SessionMessage,并将内容填入消息后,就可以用 Address确定信件地址了。和Message一样,Address也是个抽象类。您用的是javax.mail.internet.InternetAddress类。

若创建的地址只包含电子邮件地址,只要传递电子邮件地址到构造器就行了。

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

若希望名字紧挨着电子邮件显示,也可以把它传递给构造器:

Address address = new InternetAddress("president@whitehouse.gov", "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("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 中定义的)或验证邮件交换(mail exchangeMX)记录,但这些功能不属于 JavaMail API

Authenticator

java.net类一样,JavaMail API 也可以利用Authenticator通过用户名和密码访问受保护的资源。对于JavaMail API 来说,这些资源就是邮件服务器。JavaMail Authenticatorjavax.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);

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获取消息与发送消息开始很相似。但是,在 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

基本上,对于 JavaMail API 差不多所有内容,了解如何使用这七个类已经足够。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 程序块中,这样创建和发送消息时就能够抛出异常。

练习 2. 如何发送第一条消息

在上一个练习中,您使用 JavaMail 实现提供的演示程序发送了一条邮件消息。在这个练习中,您将亲自创建程序。

需要更多练习的帮助,请参阅关于练习

先决条件:

· 练习 1. 如何设置 JavaMail 环境

框架代码:

· MailExample.java

任务 1
参照框架代码开始着手,获取系统Properties

任务 1 的帮助:

Properties props = System.getProperties();

任务 2
将您的 SMTP 服务器名添加到mail.smtp.host关键字的属性中。

任务 2 的帮助:

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

任务 3
获取基于PropertiesSession对象。

任务 3 的帮助:

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

任务 4
session 创建一个MimeMessage

任务 4 的帮助:

MimeMessage message = new MimeMessage(session);

任务 5
设置消息的from域。

任务 5 的帮助:

message.setFrom(new InternetAddress(from));

任务 6
设置消息的to域。

任务 6 的帮助:

message.addRecipient(Message.RecipientType.TO,

new InternetAddress(to));

任务 7
设置消息主题。

任务 7 的帮助:

message.setSubject("Hello JavaMail");

任务 8
设置消息内容。

任务 8 的帮助:

message.setText("Welcome to JavaMail");

任务 9
Transport发送消息。

任务 9 的帮助:

Transport.send(message);

任务 10
在命令行上编译并运行程序传递 SMTP 服务器、from地址和to地址。

任务 10 的帮助:

java MailExample SMTP.Server from@address to@address

任务 11
用标准邮件阅读程序(EudoraOutlook Expresspine...)检查,确保消息已收到。

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

public class MailExample {

public static void main (String args[]) throws Exception {

String host = args[0];

String from = args[1];

String to = args[2];

// 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);

// Set the from address

message.setFrom(new InternetAddress(from));

// Set the to address

message.addRecipient(Message.RecipientType.TO,

new InternetAddress(to));

// Set the subject

message.setSubject("Hello JavaMail");

// Set the content

message.setText("Welcome to JavaMail");

// Send message

Transport.send(message);

}

}

消息的提取

为读邮件,您获取一个会话,获取并连接一个用于邮箱的适宜的存储(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;

}

}

练习 3. 如何检查邮件

在这个练习中,我们创建了一个程序,它能够显示每条消息的from地址和主题,并提示消息内容的显示。

需要更多练习的帮助,请参阅关于练习.

先决条件:

· 练习 1. 如何设置 JavaMail 环境

框架代码

· GetMessageExample.java

任务 1
参考框架代码开始着手,获取(或说创建)一个Properties对象。

任务 1 的帮助:

Properties props = new Properties();

任务 2
获取基于PropertiesSession对象。

任务 2 的帮助:

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

任务 3
获取电子邮件协议pop3imapStore

任务 3 的帮助:

Store store = session.getStore("pop3");

任务 4
用恰当的用户名和密码连接到您的邮件主机的 store

任务 4 的帮助:

store.connect(host, username, password);

任务 5
获取您要读的 folder。很可能会是INBOX

任务 5 的帮助:

Folder folder = store.getFolder("INBOX");

任务 6
以只读方式打开 folder

任务 6 的帮助:

folder.open(Folder.READ_ONLY);

任务 7
获取一个 folder 中的消息目录。将消息列表保存在名为message的数组变量中。

任务 7 的帮助:

Message message[] = folder.getMessages();

任务 8
显示每条消息的from域和主题。

任务 8 的帮助:

System.out.println(i + ": " + message[i].getFrom()[0]

+ "/t" + message[i].getSubject());

任务 9
在提示时显示消息内容。

任务 9 的帮助:

System.out.println(message[i].getContent());

任务 10
关闭到 folder store 的连接。

任务 10 的帮助:

folder.close(false);

store.close();

任务 11
在命令行上编译并运行程序传递 SMTP 服务器、用户名和密码。对于想要阅读的消息,以 YES 响应。反之,敲击ENTER。如果想在读完所有消息前中止,可以输入 QUIT

任务 11 的帮助:

java GetMessageExample POP.Server username password

import java.io.*;

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

public class GetMessageExample {

public static void main (String args[]) throws Exception {

String host = args[0];

String username = args[1];

String password = args[2];

// Create empty properties

Properties props = new Properties();

// Get session

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

// Get the store

Store store = session.getStore("pop3");

// Connect to store

store.connect(host, username, password);

// Get folder

Folder folder = store.getFolder("INBOX");

// Open read-only

folder.open(Folder.READ_ONLY);

BufferedReader reader = new BufferedReader (

new InputStreamReader(System.in));

// Get directory

Message message[] = folder.getMessages();

for (int i=0, n=message.length; i<n; i++) {

// Display from field and subject

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)) {

// Display message content

System.out.println(message[i].getContent());

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

break;

}

}

// Close connection

folder.close(false);

store.close();

}

}

消息和标志的删除

消息的删除涉及使用与消息相关的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);

一个Folderexpunge()方法可以用来删除消息。但 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。这样做并没有为消息添加新内容,仅仅将fromreply-to(被回复人)头复制给新的收件人。这种方法用一个 boolean 参数指定消息只回复给发件人(false)或回复给全体(true)。

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

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

reply.setText("Thanks");

Transport.send(reply);

在发送消息时要配置reply to(被回复人)地址,可以用setReplyTo()方法。

练习 4. 如何回复邮件

在这个练习中,我们创建了一个程序,它能够创建一个固定格式的回复消息,并将纯文本的原始消息附加到消息中。

需要更多练习的帮助,请参阅关于练习

先决条件:

· 练习 3. 如何检查邮件

框架代码:

· ReplyExample.java

任务 1
框架代码
中已含有代码,以便从 folder 中获取消息清单并提示您创建一条回复消息。

任务 2
如果回答,则基于原始消息创建一个新的MimeMessage

任务 2 的帮助:

MimeMessage reply = (MimeMessage)message[i].reply(false);

任务 3
在您的电子邮件地址上设置from域。

任务 4
创建要回复的文本。从引入固定格式的消息开始。若原始消息是纯文本,就在原始消息的每行前添加"> " 字符并添加到回复的消息中。

任务 4 的帮助:
对于纯文本消息,请用mimeMessage.isMimeType("text/plain")检查 MIME 类型。

任务 5
一旦消息内容已完全确定,就可以对其进行设置。

任务 6
发送消息。

任务 7
在命令行上编译并运行程序传递邮件服务器、 SMTP 服务器、用户名、密码以及from地址。对您将回复的消息,响应 YES。反之,敲击ENTER。如果想在回复完所有消息前中止,可以输入 QUIT

任务 7 的帮助:

java ReplyExample POP.Server SMTP.Server username password from@address

任务 8
请检查以确认用标准邮件阅读程序(EudoraOutlook Expresspine...)收到了消息。

import java.io.*;

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

publicclass ReplyExample {

publicstaticvoid main (String args[]) throws Exception {

String host = args[0];

String sendHost = args[1];

String username = args[2];

String password = args[3];

String from = args[4];

// Create empty properties

Properties props = System.getProperties();

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

// 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);

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 reply to the message? [YES to reply/QUIT to end]");

String line = reader.readLine();

if ("YES".equals(line)) {

// Create a reply message

MimeMessage reply = (MimeMessage)message[i].reply(false);

// Set the from field

reply.setFrom(new InternetAddress(from));

// Create the reply content, copying over the original if text

MimeMessage orig = (MimeMessage)message[i];

StringBuffer buffer = new StringBuffer("Thanks/n/n");

if (orig.isMimeType("text/plain")) {

String content = (String)orig.getContent();

StringReader contentReader = new StringReader(content);

BufferedReader br = new BufferedReader(contentReader);

String contentLine;

while ((contentLine = br.readLine()) != null) {

buffer.append("> ");

buffer.append(contentLine);

buffer.append("/r/n");

}

}

// Set the content

reply.setText(buffer.toString());

// Send the message

Transport.send(reply);

} elseif ("QUIT".equals(line)) {

break;

}

}

// Close connection

folder.close(false);

store.close();

}

}

附件的处理

附件是邮件消息的相关资源,如通常不包含在消息正文里文本文件、电子表格或图像等。常见的邮件程序,如 Eudora pine 之类,可以用 JavaMail API 将资源attach(附加)到您的消息上,就可以在收到消息时得到。

附件的发送:
发送附件非常像转发消息。您建立各部分以组成完整消息。完成第一部件,即消息正文后,您添加其它部件,其中每个DataHandler都代表附件,而不是转发消息情况下的共享处理程序。如果从文件中读附件,附件的数据源是FileDataSource。而如果从 URL 中读时,附件的数据源是URLDataSource。一旦存在DataSource,只要先把它传递给DataHandler构造器,最后再用setDataHandler()把它附加到BodyPart。假定您要保留附件的原始文件名,最终要做的是用BodyPartsetFileName()方法设置与附件相关的文件名。如下所示:

// 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 堆大小。

练习:
练习
5. 如何发送附件

附件的获取:
从消息中获取附件比发送它们棘手些,因为 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, ...

}

...

}

练习 5. 如何发送附件

在这个练习中,我们创建了一个程序,它能够发送含附件的消息。

需要更多练习的帮助,请参阅关于练习

先决条件:

· 练习 2. 如何发送第一条消息

框架代码:

· AttachExample.java

任务 1
skeleton code
中已经包含代码以获取初始邮件 session

任务 2
根据 session 获取一个Message并设置它的头部域:tofrom subject(主题)。

任务 3
为消息正文内容创建BodyPart,并以消息正文为内容。

任务 3 的帮助:

BodyPart messageBodyPart = new MimeBodyPart();

messageBodyPart.setText("Here's the file");

任务 4
创建一个Multipart从而将正文内容和附件结合。将正文内容添加到 multipart 中。

任务 4 的帮助:

Multipart multipart = new MimeMultipart();

multipart.addBodyPart(messageBodyPart);

任务 5
为附件创建第二个BodyPart

任务 6
获取作为DataSource的附件。

任务 6 的帮助:

DataSource source = new FileDataSource(filename);

任务 7
?
将数据源设置为消息部分的DataHandler。同时设置原始文件名。

任务 7 的帮助:

messageBodyPart.setDataHandler(new DataHandler(source));

messageBodyPart.setFileName(filename);

任务 8
将消息的第二部件添加到 multipart

任务 9
将消息内容设置成 multipart

任务 9 的帮助:

message.setContent(multipart);

任务 10
发送消息。

任务 11
在命令行上编译并运行程序传递 SMTP 服务器、from地址、to地址和文件名。这就以附件形式发送了您的文件。

任务 11 的帮助:

java AttachExample SMTP.Server from@address to@address filename

任务 12
请用您的标准邮件阅读程序(EudoraOutlook Expresspine...)检查,确保消息已收到。

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

import javax.activation.*;

public class AttachExample {

public static void main (String args[]) throws Exception {

String host = args[0];

String from = args[1];

String to = args[2];

String filename = args[3];

// Get system properties

Properties props = System.getProperties();

// Setup mail server

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

// Get session

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

// 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("Here's the file");

// Create a Multipart

Multipart multipart = new MimeMultipart();

// Add part one

multipart.addBodyPart(messageBodyPart);

//

// Part two is attachment

//

// Create second body part

messageBodyPart = new MimeBodyPart();

// Get the attachment

DataSource source = new FileDataSource(filename);

// Set the data handler to the attachment

messageBodyPart.setDataHandler(new DataHandler(source));

// Set the filename

messageBodyPart.setFileName(filename);

// Add part two

multipart.addBodyPart(messageBodyPart);

// Put parts in message

message.setContent(multipart);

// Send the message

Transport.send(message);

}

}

HTML 消息的处理

发送基于 HTML 文件格式消息的工作量比发送纯文本消息多,虽然不一定非要这些多余的工作量。如何选择完全取决于给定的请求。

HTML 消息的发送:
若您所要做的全部事情是发送一份 HTML 文件的等价物作为消息,但让邮件阅读者为不能提取任何内嵌图像或相关片段而担心的话,可以使用MessagesetContent()方法,把内容当作一个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头设置成随机字符串,作为图像的srcimg标记中使用。完整的演示如下。

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);

练习 6. 如何发送含图像的 HTML 消息

在这个练习中,我们创建了一个程序,它能够发送含图像附件(图像显示在 HTML 消息内部)的 HTML 消息。

需要更多练习的帮助,请参阅关于练习

先决条件:

· 练习 5. 如何发送附件

框架代码:

· logo.gif

· HtmlImageExample.java

任务 1
框架代码
已经包含代码以获取初始邮件 session、创建消息正文以及填充消息头(tofrom subject)。

任务 2
创建一个 HTML 文件格式消息内容的BodyPart

任务 3
创建一个 HTML 文件格式内容的文本字符串。在 HTML 文件中引入一个图像的引用(<img src="...">),它和邮件消息处于同一目录。

任务 3 的帮助:
使用一个cid URL。图像的 content-id 可以稍后再定。

String htmlText = "<H1>Hello</H1>" +

"<img src=/"cid:memememe/">";

任务 4
设置消息部件的内容。确保将 MIME 的类型定为text/html

任务 4 的帮助:

messageBodyPart.setContent(htmlText, "text/html");

任务 5
创建一个Multipart从而将正文内容和附件结合。确保已指定相关部分。然后正文内容将添加到 multipart 中。

任务 5 的帮助:

MimeMultipart multipart = new MimeMultipart("related");

multipart.addBodyPart(messageBodyPart);

任务 6
为附件创建第二个BodyPart

任务 7
获取作为DataSource的附件,并把消息部件的数据源设置为该DataHandler

任务 8
设置Content-ID头,使 HTML 文件中给定的部件匹配图像引用。

任务 8 的帮助:

messageBodyPart.setHeader("Content-ID","memememe");

任务 9
将消息的第二部件添加到 multipart,并将消息的内容设置成 multipart

任务 10:
发送消息。

任务 11
在命令行上编译并运行程序传递 SMTP 服务器、from地址、to地址和文件名。这样会把图片当作 HTML 文本中内嵌的图片发送。

任务 11 的帮助:

java HtmlImageExample SMTP.Server from@address to@address filename

任务 12
请检查邮件阅读程序辨认出的消息是否为 HTML 格式文件,而且消息中显示的是图片,不是外部附件文件的链接。

任务 12 的帮助:
如果邮件阅读程序无法显示 HTML 格式文件的消息,可以考虑把它发给朋友(让他们试试)。

import java.util.Properties;

import javax.mail.*;

import javax.mail.internet.*;

import javax.activation.*;

public class HtmlImageExample {

public static void main (String args[]) throws Exception {

String host = args[0];

String from = args[1];

String to = args[2];

String file = args[3];

// Get system properties

Properties props = System.getProperties();

// Setup mail server

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

// Get session

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

// 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();

// Set the HTML content, be sure it references the attachment

String htmlText = "<H1>Hello</H1>" +

"<img src=/"cid:memememe/">";

// Set the content of the body part

messageBodyPart.setContent(htmlText, "text/html");

// Create a related multi-part to combine the parts

MimeMultipart multipart = new MimeMultipart("related");

// Add body part to multipart

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));

// Add a header to connect to the HTML

messageBodyPart.setHeader("Content-ID","<memememe>");

// Add part to multi-part

multipart.addBodyPart(messageBodyPart);

// Associate multi-part with message

message.setContent(multipart);

// Send message

Transport.send(message);

}

}

总结

JavaMail API 是一个 Java 包,用于阅读、编写及发送电子邮件消息(包括附件)。您能用它建立基于标准的电子邮件客户机,它配置了各种因特网邮件协议包括 SMTPPOPIMAP MIME,还包括相关的 NNTPS/MIME 及其它协议。

API 自然的划分为两个部件。第一部件着眼于独立于使用协议的发送、接收及管理消息,而第二部件则着眼于协议的特定用途。本教程的目的在于展示如何利用 API 的第一部分,并不试图涉及协议供应商。

核心 JavaMail API 由七个类组成 SessionMessageAddressAuthenticatorTransportStoreFolder它们都来自javax.mail、即 JavaMail API 顶级包。我们用这些类完成了大量常见的电子邮件任务,包括发送消息、检索消息、删除消息、认证、回复消息、转发消息、管理附件、处理基于 HTML 文件格式的消息以及搜索或过滤邮件列表。

最后,提供了大量按部就班的练习来协助说明提出的概念。希望会有助于您将电子邮件功能性添加到独立于平台的 Java 应用程序。

JavaMail API,您所能做的比这里多得多。以下资源是这个课程和练习的补充:

· JavaMail API home page 下载 JavaMail 1.2 API

· JavaMail API 版本 1.2 1.1.3 需要JavaBeans Activation Framework 的支持。

· JavaMail-interest mailing list Sun 的开发者论坛。

· Sun JavaMail FAQ applet servlet 的形式提出了 JavaMail 的用途,还提出了特定于协议的问题。

· 教程作者 John Zukowski jGuru's JavaMail FAQ 站点进行维护。

· 想知道其它用户怎么使用 JavaMail 吗?请查看 Sun 第三方产品

· 如果您需要更多 JavaMail 的详细信息,请阅读 Rick Grehan "How JavaMail keeps it simple"Lotus Developer Network2000 6 月)。

· Benoit Marchal 提出如何用 Java XML 实现纯文本及 HTML 文件格式的时事通讯,请参考这个由两部分组成的系列“Managing e-zines with JavaMail and XSLT”第一部分developerWorks2001 3 月)和 2 部分 developerWorks2001 4 月)。

· "Linking Applications with E-mail"Lotus Developer Network2000 5 月)论述了群件如何促进应用程序间的交流、协作及配合。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值