一、
JavaMail API
简介
JavaMail API
是读取、撰写、发送电子信息的可选包。我们可用它来建立如
Eudora
、
Foxmail
、
MS Outlook Express
一般的邮件用户代理程序(
Mail User Agent,
简称
MUA
)。而不是像
sendmail
或者其它的邮件传输代理(
Mail Transfer Agent
,简称
MTA
)程序那样可以传送、递送、转发邮件。从另外一个角度来看,我们这些电子邮件用户日常用
MUA
程序来读写邮件,而
MUA
依赖着
MTA
处理邮件的递送。
在清楚了到
MUA
与
MTA
之间的关系后,让我们看看
JavaMail API
是如何提供信息访问功能的吧!
JavaMail API
被设计用于以不依赖协议的方式去发送和接收电子信息,这个
API
被分为两大部分:
基本功能:如何以不依赖于协议的方式发送接收电子信息,这也是本文所要描述的,不过在下文中,大家将看到这只是一厢情愿而已。
第二个部分则是依赖特定协议的,比如
SMTP
、
POP
、
IMAP
、
NNTP
协议。在这部分的
JavaMail API
是为了和服务器通讯,并不在本文的内容中。
二、相关协议一览
在我们步入
JavaMail API
之前,先看一下
API
所涉及的协议。以下便是大家日常所知、所乐于使用的
4
大信息传输协议:
SMTP
POP
IMAP
MIME
当然,上面的
4
个协议,并不是全部,还有
NNTP
和其它一些协议可用于传输信息,但是由于不常用到,所以本文便不提及了。理解这
4
个基本的协议有助于我们更好的使用
JavaMail API
。然而
JavaMail API
是被设计为与协议无关的,目前我们并不能克服这些协议的束缚。确切的说,如果我们使用的功能并不被我们选择的协议支持,那么
JavaMail API
并不可能如魔术师一样神奇的赋予我们这种能力。
1
.
SMTP
简单邮件传输协议定义了递送邮件的机制。在下文中,我们将使用基于
Java-Mail
的程序与公司或者
ISP
的
SMTP
服务器进行通讯。这个
SMTP
服务器将邮件转发到接收者的
SMTP
服务器,直至最后被接收者通过
POP
或者
IMAP
协议获取。这并不需要
SMTP
服务器使用支持授权的邮件转发,但是却的确要注意
SMTP
服务器的正确设置(
SMTP
服务器的设置与
JavaMail API
无关)。
2
.
POP
POP
是一种邮局协议,目前为第
3
个版本,即众所周知的
POP3
。
POP
定义了一种用户如何获得邮件的机制。它规定了每个用户使用一个单独的邮箱。大多数人在使用
POP
时所熟悉的功能并非都被支持,例如查看邮箱中的新邮件数量。而这个功能是微软的
Outlook
内建的,那么就说明微软
Outlook
之类的邮件客户端软件是通过查询最近收到的邮件来计算新邮件的数量来实现前面所说的功能。因此在我们使用
JavaMail API
时需要注意,当需要获得如前面所讲的新邮件数量之类的信息时,我们不得不自己进行计算。
3
.
IMAP
IMAP
使用在接收信息的高级协议,目前版本为第
4
版,所以也被称为
IMAP4
。需要注意的是在使用
IMAP
时,邮件服务器必须支持该协议。从这个方面讲,我们并不能完全使用
IMAP
来替代
POP
,不能期待
IMAP
在任何地方都被支持。假如邮件服务器支持
IMAP
,那么我们的邮件程序将能够具有以下被
IMAP
所支持的特性:每个用户在服务器上可具有多个目录,这些目录能在多个用户之间共享。
其与
POP
相比高级之处显而易见,但是在尝试采取
IMAP
时,我们认识到它并不是十分完美的:由于
IMAP
需要从其它服务器上接收新信息,将这些信息递送给用户,维护每个用户的多个目录,这都为邮件服务器带来了高负载。并且
IMAP
与
POP
的一个不同之处是
POP
用户在接收邮件时将从邮件服务器上下载邮件,而
IMAP
允许用户直接访问邮件目录,所以在邮件服务器进行备份作业时,由于每个长期使用此邮件系统的用户所用的邮件目录会占有很大的空间,这将直接导致邮件服务器上磁盘空间暴涨。
4
.
MIME
MIME
并不是用于传送邮件的协议,它作为多用途邮件的扩展定义了邮件内容的格式:信息格式、附件格式等等。一些
RFC
标准都涉及了
MIME
:
RFC 822, RFC 2045, RFC 2046, RFC 2047
,有兴趣的
Matrixer
可以阅读一下。而作为
JavaMail API
的开发者,我们并不需关心这些格式定义,但是这些格式被用在了程序中。
5
.
NNTP
和其它的第三方协议
正因为
JavaMail API
在设计时考虑到与第三方协议实现提供商之间的分离,故我们可以很容易的添加一些第三方协议。
SUN
维护着一个第三方协议实现提供商的列表:
http://java.sun.com/products/javamail/Third_Party.html
,通过此列表我们可以找到所需要的而又不被
SUN
提供支持的第三方协议:比如
NNTP
这个新闻组协议和
S/MIME
这个安全的
MIME
协议。
三、安装
1
.安装
JavaMail
为了使用
JavaMail API
,需要从
http://java.sun.com/products/javamail/downloads/index.html
下载文件名格式为
javamail-[version].zip
的文件(这个文件中包括了
JavaMail
实现),并将其中的
mail.jar
文件添加到
CLASSPATH
中。这个实现提供了对
SMTP
、
IMAP4
、
POP3
的支持。
注意:在安装
JavaMail
实现之后,我们将在
demo
目录中发现许多有趣的简单实例程序。
在安装了
JavaMail
之后
,
我们还需要安装
JavaBeans Activation Framework
,因为这个框架是
JavaMail API
所需要的。如果我们使用
J2EE
的话,那么我们并无需单独下载
JavaMail
,因为它存在于
J2EE.jar
中,只需将
J2EE.jar
加入到
CLASSPATH
即可。
2
.安装
JavaBeans Activation Framework
从
http://java.sun.com/products/javabeans/glasgow/jaf.html
下载
JavaBeans Activation Framework
,并将其添加到
CLASSPATH
中。此框架增加了对任何数据块的分类、以及对它们的处理的特性。这些特性是
JavaMail API
需要的。虽然听起来这些特性非常模糊,但是它对于我们的
JavaMail API
来说只是提供了基本的
MIME
类型支持。
到此为止,我们应当把
mail.jar
和
activation.jar
都添加到了
CLASSPATH
中。
当然如果从方便的角度讲,直接把这两个
Jar
文件复制到
JRE
目录的
lib/ext
目录中也可以。
四、初次认识
JavaMail API
1
.了解我们的
JavaMail
环境
A
.纵览
JavaMail
核心类结构
打开
JavaMail.jar
文件,我们将发现在
javax.mail
的包下面存在着一些核心类:
Session
、
Message
、
Address
、
Authenticator
、
Transport
、
Store
、
Folder
。而且在
javax.mail.internet
包中还有一些常用的子类。
B
.
Session
Session
类定义了基本的邮件会话。就像
Http
会话那样,我们进行收发邮件的工作都是基于这个会话的。
Session
对象利用了
java.util.Properties
对象获得了邮件服务器、用户名、密码信息和整个应用程序都要使用到的共享信息。
Session
类的构造方法是私有的,所以我们可以使用
Session
类提供的
getDefaultInstance()
这个静态工厂方法获得一个默认的
Session
对象:
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.getInstance(props, null);
从上面的两个例子中不难发现,
getDefaultInstance()
和
getInstance()
方法的第二个参数都是
null
,这是因为在上面的例子中并没有使用到邮件授权,下文中将对授权进行详细介绍。
从很多的实例看,在对
mail server
进行访问的过程中使用共享的
Session
是足够的,即使是工作在多个用户邮箱的模式下也不例外。
C
.
Message
当我们建立了
Session
对象后,便可以被发送的构造信息体了。在这里
SUN
提供了
Message
类型来帮助开发者完成这项工作。由于
Message
是一个抽象类,大多数情况下,我们使用
javax.mail.internet.MimeMessage
这个子类,该类是使用
MIME
类型、
MIME
信息头的邮箱信息。信息头只能使用
US-ASCII
字符,而非
ASCII
字符将通过编码转换为
ASCII
的方式使用。
为了建立一个
MimeMessage
对象,我们必须将
Session
对象作为
MimeMessage
构造方法的参数传入:
MimeMessage message = new MimeMessage(session);
注意:对于
MimeMessage
类来讲存在着多种构造方法,比如使用输入流作为参数的构造方法。
在建立了
MimeMessage
对象后,我们需要设置它的各个
part
,对于
MimeMessage
类来说,这些
part
就是
MimePart
接口。最基本的设置信息内容的方法就是通过表示信息内容和米么类型的参数调用
setContent()
方法:
message.setContent("Hello", "text/plain");
然而,如果我们所使用的
MimeMessage
中信息内容是文本的话,我们便可以直接使用
setText()
方法来方便的设置文本内容。
message.setText("Hello");
前面所讲的两种方法,对于文本信息,后者更为合适。而对于其它的一些信息类型,比如
HTML
信息,则要使用前者。
别忘记了,使用
setSubject()
方法对邮件设置邮件主题:
message.setSubject("First");
D
.
Address
到这里,我们已经建立了
Session
和
Message
,下面将介绍如何使用邮件地址类:
Address
。像
Message
一样,
Address
类也是一个抽象类,所以我们将使用
javax.mail.internet.InternetAddress
这个子类。
通过传入代表邮件地址的字符串,我们可以建立一个邮件地址类:
Address address = new InternetAddress("[email protected]");
如果要在邮件地址后面增加名字的话,可以通过传递两个参数:代表邮件地址和名字的字符串来建立一个具有邮件地址和名字的邮件地址类:
Address address = new InternetAddress("[email protected]", "George Bush");
本文在这里所讲的邮件地址类是为了设置邮件信息的发信人和收信人而准备的,在建立了邮件地址类后,我们通过
message
的
setFrom()
和
setReplyTo()
两种方法设置邮件的发信人:
message.setFrom(address);
message.setReplyTo(address);
若在邮件中存在多个发信人地址,我们可用
addForm()
方法增加发信人:
Address address[ ] = ...;message.addFrom(address);
为了设置收信人,我们使用
addRecipient()
方法增加收信人,此方法需要使用
Message.RecipientType
的常量来区分收信人的类型:
message.addRecipient(type, address)
下面是
Message.RecipientType
的三个常量
:
Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC
因此,如果我们要发送邮件给总统,并发用一个副本给第一夫人的话,下面的方法将被用到:
Address toAddress = new InternetAddress([email protected]);
message.addRecipient(Message.RecipientType.TO, toAddress);
message.addRecipient(Message.RecipientType.CC, ccAddress);
JavaMail API
并没有提供检查邮件地址有效性的机制。当然我们可以自己完成这个功能:验证邮件地址的字符是否按照
RFC822
规定的格式书写或者通过
DNS
服务器上的
MX
记录验证等。
E
.
Authenticator
像
java.net
类那样,
JavaMail API
通过使用授权者类(
Authenticator
)以用户名、密码的方式访问那些受到保护的资源,在这里
“
资源
”
就是指邮件服务器。在
javax.mail
包中可以找到这个
JavaMail
的授权者类(
Authenticator
)。
在使用
Authenticator
这个抽象类时,我们必须采用继承该抽象类的方式,并且该继承类必须具有返回
PasswordAuthentication
对象(用于存储认证时要用到的用户名、密码)
getPasswordAuthentication()
方法。并且要在
Session
中进行注册,使
Session
能够了解在认证时该使用哪个类。
下面代码片断中的
MyAuthenticator
就是一个
Authenticator
的子类。
Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
F
.
Transport
在发送信息时,
Transport
类将被用到。这个类实现了发送信息的协议(通称为
SMTP
),此类是一个抽象类,我们可以使用这个类的静态方法
send()
来发送消息:
Transport.send(message);
当然,方法是多样的。我们也可由
Session
获得相应协议对应的
Transport
实例。并通过传递用户名、密码、邮件服务器主机名等参数建立与邮件服务器的连接,并使用
sendMessage()
方法将信息发送,最后关闭连接:
message.saveChanges();
// implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();
评论:上面的方法是一个很好的方法,尤其是在我们在同一个邮件服务器上发送多个邮件时。因为这时我们将在连接邮件服务器后连续发送邮件,然后再关闭掉连接。
send()
这个基本的方法是在每次调用时进行与邮件服务器的连接的,对于在同一个邮件服务器上发送多个邮件来讲可谓低效的方式。
注意:如果需要在发送邮件过程中监控
mail
命令的话,可以在发送前设置
debug
标志:
session.setDebug(true)
。
G
.
Store
和
Folder
接收邮件和发送邮件很类似都要用到
Session
。但是在获得
Session
后,我们需要从
Session
中获取特定类型的
Store
,然后连接到
Store
,这里的
Store
代表了存储邮件的邮件服务器。在连接
Store
的过程中,极有可能需要用到用户名、密码或者
Authenticator
。
// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);
在连接到
Store
后,一个
Folder
对象即目录对象将通过
Store
的
getFolder()
方法被返回,我们可从这个
Folder
中读取邮件信息:
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
上面的例子首先从
Store
中获得
INBOX
这个
Folder
(对于
POP3
协议只有一个名为
INBOX
的
Folder
有效),然后以只读(
Folder.READ_ONLY
)的方式打开
Folder
,最后调用
Folder
的
getMessages()
方法得到目录中所有
Message
的数组。
注意:对于
POP3
协议只有一个名为
INBOX
的
Folder
有效,而对于
IMAP
协议,我们可以访问多个
Folder
(想想前面讲的
IMAP
协议)。而且
SUN
在设计
Folder
的
getMessages()
方法时采取了很智能的方式:首先接收新邮件列表,然后再需要的时候(比如读取邮件内容)才从邮件服务器读取邮件内容。
在读取邮件时,我们可以用
Message
类的
getContent()
方法接收邮件或是
writeTo()
方法将邮件保存,
getContent()
方法只接收邮件内容(不包含邮件头),而
writeTo()
方法将包括邮件头。
System.out.println(((MimeMessage)message).getContent());
在读取邮件内容后,别忘记了关闭
Folder
和
Store
。
folder.close(aBoolean);
store.close();
传递给
Folder.close()
方法的
boolean
类型参数表示是否在删除操作邮件后更新
Folder
。
H
.继续向前进!
在讲解了以上的七个
Java Mail
核心类定义和理解了简单的代码片断后,下文将详细讲解怎样使用这些类实现
JavaMail API
所要完成的高级功能。
五、使用
JavaMail API
在明确了
JavaMail API
的核心部分如何工作后,本人将带领大家学习一些使用
Java Mail API
任务案例。
1
.发送邮件
在获得了
Session
后,建立并填入邮件信息,然后发送它到邮件服务器。这便是使用
Java Mail API
发送邮件的过程,在发送邮件之前,我们需要设置
SMTP
服务器:通过设置
Properties
的
mail.smtp.host
属性。
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
.接收邮件
为了在读取邮件,我们获得了
session
,并且连接到了邮箱的相应
store
,打开相应的
Folder
,然后得到我们想要的邮件,当然别忘记了在结束时关闭连接。
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();
f
or (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();
上面的代码所作的是从邮箱中读取每个邮件,并且显示邮件的发信人地址和主题。从技术角度讲,这里存在着一个异常的可能:当发信人地址为空时,
getFrom()[0]
将抛出异常。
下面的代码片断有效的说明了如何读取邮件内容,在显示每个邮件发信人和主题后,将出现用户提示从而得到用户是否读取该邮件的确认,如果输入
YES
的话,我们可用
Message.writeTo(java.io.OutputStream os)
方法将邮件内容输出到控制台上,关于
Message.writeTo()
的具体用法请看
JavaMail API
。
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
.删除邮件和标志
设置与
message
相关的
Flags
是删除邮件的常用方法。这些
Flags
表示了一些系统定义和用户定义的不同状态。在
Flags
类的内部类
Flag
中预定义了一些标志:
Flags.Flag.ANSWERED
Flags.Flag.DELETED
Flags.Flag.DRAFT
Flags.Flag.FLAGGED
Flags.Flag.RECENT
Flags.Flag.SEEN
Flags.Flag.USER
但需要在使用时注意的:标志存在并非意味着这个标志被所有的邮件服务器所支持。例如,对于删除邮件的操作,
POP
协议不支持上面的任何一个。所以要确定哪些标志是被支持的
??
通过访问一个已经打开的
Folder
对象的
getPermanetFlags()
方法,它将返回当前被支持的
Flags
类对象。
删除邮件时,我们可以设置邮件的
DELETED
标志:
message.setFlag(Flags.Flag.DELETED, true);
但是首先要采用
READ_WRITE
的方式打开
Folder
:
folder.open(Folder.READ_WRITE);
在对邮件进行删除操作后关闭
Folder
时,需要传递一个
true
作为对删除邮件的擦除确认。
folder.close(true);
Folder
类中另一种用于删除邮件的方法
expunge()
也同样可删除邮件,但是它并不为
sun
提供的
POP3
实现支持,而其它第三方提供的
POP3
实现支持或者并不支持这种方法。
另外,介绍一种检查某个标志是否被设置的方法:
Message.isSet(Flags.Flag flag)
方法,其中参数为被检查的标志。
4
.邮件认证
我们在前面已经学会了如何使用
Authenticator
类来代替直接使用用户名和密码这两字符串作为
Session.getDefaultInstance()
或者
Session.getInstance()
方法的参数。在前面的小试牛刀后,现在我们将了解到全面认识一下邮件认证。
我们在此取代了直接使用邮件服务器主机名、用户名、密码这三个字符串作为连接到
POP3 Store
的方式,使用存储了邮件服务器主机名信息的属性文件,并在获得
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();
PopupAuthenticator
类继承了抽象类
Authenticator
,并且通过重载
Authenticator
类的
getPasswordAuthentication()
方法返回
PasswordAuthentication
类对象。而
getPasswordAuthentication()
方法的参数
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
.回复邮件
回复邮件的方法很简单:使用
Message
类的
reply()
方法,通过配置回复邮件的收件人地址和主题(如果没有提供主题的话,系统将默认将
“Re
:
”
作为邮件的主体),这里不需要设置任何的邮件内容,只要复制发信人或者
reply-to
到新的收件人。而
reply()
方法中的
boolean
参数表示是否将邮件回复给发送者(参数值为
false
),或是恢复给所有人(参数值为
true
)。
补充一下,
reply-to
地址需要在发信时使用
setReplyTo()
方法设置。
MimeMessage reply = (MimeMessage)message.reply(false); reply.setFrom(new InternetAddress("[email protected]")); reply.setText("Thanks");Transport.send(reply);
6
.转发邮件
转发邮件的过程不如前面的回复邮件那样简单,它将建立一个转发邮件,这并非一个方法就能做到。
每个邮件是由多个部分组成,每个部分称为一个邮件体部分,是一个
BodyPart
类对象,对于
MIME
类型邮件来讲就是
MimeBodyPart
类对象。这些邮件体包含在成为
Multipart
的容器中对于
MIME
类型邮件来讲就是
MimeMultiPart
类对象。在转发邮件时,我们建立一个文字邮件体部分和一个被转发的文字邮件体部分,然后将这两个邮件体放到一个
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 contentmessage
BodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());
// Add part to multi part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with messageforward.
setContent(multipart);
// Send message
Transport.send(forward);
7
.使用附件
附件作为与邮件相关的资源经常以文本、表格、图片等格式出现,如流行的邮件客户端一样,我们可以用
JavaMail API
从邮件中获取附件或是发送带有附件的邮件。
A
.发送带有附件的邮件
发送带有附件的邮件的过程有些类似转发邮件,我们需要建立一个完整邮件的各个邮件体部分,在第一个部分(即我们的邮件内容文字)后,增加一个具有
DataHandler
的附件而不是在转发邮件时那样复制第一个部分的
DataHandler
。
如果我们将文件作为附件发送,那么要建立
FileDataSource
类型的对象作为附件数据源;如果从
URL
读取数据作为附件发送,那么将要建立
URLDataSource
类型的对象作为附件数据源。
然后将这个数据源(
FileDataSource
或是
URLDataSource
)对象作为
DataHandler
类构造方法的参数传入,从而建立一个
DataHandler
对象作为数据源的
DataHandler
。
接着将这个
DataHandler
设置为邮件体部分的
DataHandler
。这样就完成了邮件体与附件之间的关联工作,下面的工作就是
BodyPart
的
setFileName()
方法设置附件名为原文件名。
最后将两个邮件体放入到
Multipart
中,设置邮件内容为这个容器
Multipart
,发送邮件。
// 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 messagemessage
BodyPart.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 message
message.setContent(multipart);
// Send the message
Transport.send(message);
如果我们使用
servlet
实现发送带有附件的邮件,则必须上传附件给
servlet
,这时需要注意提交页面
form
中对编码类型的设置应为
multipart/form-data
。
<form enctype="multipartform-data" method='post action="/myservlet"'>
<input type="file"bane="thefile">
<input type= "submit" value="upload">
B
.读取邮件中的附件
读取邮件中的附件的过程要比发送它的过程复杂一点。因为带有附件的邮件是多部分组成的,我们必须处理每一个部分获得邮件的内容和附件。
但是如何辨别邮件信息内容和附件呢?
Sun
在
Part
类(
BodyPart
类实现的接口类)中提供了
getDisposition()
方法让开发者获得邮件体部分的部署类型,当该部分是附件时,其返回之将是
Part.ATTACHMENT
。但附件也可以没有部署类型的方式存在或者部署类型为
Part.INLINE
,无论部署类型为
Part.ATTACHMENT
还是
Part.INLINE
,我们都能把该邮件体部分导出保存。
Multipart mp = (Multipart)message.getContent();
for (int i=0, n=multipart.getCount(); i<n; i++)
{
Part part = Multipart.getBodypart(i));
String disposition = part.getDisposition ();
IF ((disposition != null) && ((disposition.equals(part.attachment) || (disposition.equals(part.inline))) {
savefile(part.getFilename(),
part.getInputStream());}}
下列代码中使用了
saveFile
方法是自定义的方法,它根据附件的文件名建立一个文件,如果本地磁盘上存在名为附件的文件,那么将在文件名后增加数字表示区别。然后从邮件体中读取数据写入到本地文件中(代码省略)。
// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
file = new File(filename+i);
}
以上是邮件体部分被正确设置的简单例子,如果邮件体部分的部署类型为
null
,那么我们通过获得邮件体部分的
MIME
类型来判断其类型作相应的处理,代码结构框架如下:
if (disposition == null) {
// Check if plain
MimeBodyPart mbp = (MimeBodyPart)part;
if (mbp.isMimeType("text/plain")) {
// Handle plain
} else {
// Special non-attachment cases here of
// image/gif, text/html, ... }
...}
8
.处理
HTML
邮件
前面的例子中发送的邮件都是以文本为内容的(除了附件),下面将介绍如何接收和发送基于
HTML
的邮件。
A
.发送
HTML
邮件
假如我们需要发送一个
HTML
文件作为邮件内容,并使邮件客户端在读取邮件时获取相关的图片或者文字的话,只要设置邮件内容为
html
代码,并设置内容类型为
text/html
即可:
String htmlText = " Hello " +message.setContent(htmlText, "text/html"));
请注意:这里的图片并不是在邮件中内嵌的,而是在
URL
中定义的。邮件接收者只有在线时才能看到。
在接收邮件时,如果我们使用
JavaMail API
接收邮件的话是无法实现以
HTML
方式显示邮件内容的。因为
JavaMail API
邮件内容视为二进制流。所以要显示
HTML
内容的邮件,我们必须使用
JEditorPane
或者第三方
HTML
展现组件。
以下代码显示了如何使用
JEditorPane
显示邮件内容:
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
作为内容,那么最好将
HTML
中使用的图片作为邮件的一部分,这样无论是否在线都会正确的显示
HTML
中的图片。处理方法就是将
HTML
中用到的图片作为邮件附件并使用特殊的
cid URL
作为图片的引用,这个
cid
就是对图片附件的
Content-ID
头的引用。
处理内嵌图片就像向邮件中添加附件一样,不同之处在于我们必须通过设置图片附件所在的邮件体部分的
header
中
Content-ID
为一个随机字符串,并在
HTML
中
img
的
src
标记中设置为该字符串。这样就完成了图片附件与
HTML
的关联。
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 = " Hello" + "<CCID_FILE VALUES="/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 imagemessageBodyPart = new MimeBodyPart();
// Fetch the image and associate to par
tDataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","");
// Add part to multi-part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message.setContent(multipart);
9
.在邮件中搜索短语
JavaMail API
提供了过滤器机制,它被用来建立搜索短语。这个短语由
javax.mail.search
包中的
SearchTerm
抽象类来定义,在定义后我们便可以使用
Folder
的
Search()
方法在
Folder
中查找邮件:
SearchTerm st = ...;Message[] msgs = folder.search(st);
下面有
22
个不同的类(继承了
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.)
使用这些类定义的断语集合,我们可以构造一个逻辑表达式,并在
Folder
中进行搜索。下面是一个实例:在
Folder
中搜索邮件主题含有
“ADV”
字符串或者发信人地址为
[email protected]
的邮件。
SearchTerm st = new OrTerm( new SubjectTerm("ADV:"),
Message[] msgs = folder.search(st);
六、参考资源
JavaMail API Home
Sun’s JavaMail API
基础
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