JavaMail API是读取、撰写、发送电子信息的可选包。咱们可用它来创建如Eudora、Foxmail、MS Outlook Express通常的邮件用户代理程序(Mail User Agent,简称MUA)。让咱们看看JavaMail API是如何提供信息访问功能的吧!JavaMail API被设计用于以不依赖协议的方式去发送和接收电子信息,文中着重:如何以不依赖于协议的方式发送接收电子信息,这也是本文所要描述的.html
版权声明:本文能够自由转载,转载时请务必以超连接形式标明文章原始出处和做者信息及本声明java
做者:cleverpig(做者的Blog:http://blog.matrix.org.cn/page/cleverpig)数组
原文:http://www.matrix.org.cn/resource/article/44/44101_JavaMail.html安全
关键字:java,mail,pop,smtp服务器
1、JavaMail API简介session
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被分为两大部分:框架
基本功能:如何以不依赖于协议的方式发送接收电子信息,这也是本文所要描述的,不过在下文中,你们将看到这只是一厢情愿而已。ide
第二个部分则是依赖特定协议的,好比SMTP、POP、IMAP、NNTP协议。在这部分的JavaMail API是为了和服务器通信,并不在本文的内容中。学习
2、相关协议一览
在咱们步入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协议。
3、安装
1.安装JavaMail
为了使用JavaMail API,须要从http://java.sun.com/products/javamail/downloads/index.html下载文件名格式为javamail-[version].zip的文件(这个文件中包括了JavaMail实现),并将其中的mail.jar文件添加到CLASSPATH中。这个实现提供了对SMTP、IMAP四、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目录中也能够。
4、初次认识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("president@whitehouse.gov");
若是要在邮件地址后面增长名字的话,能够经过传递两个参数:表明邮件地址和名字的字符串来创建一个具备邮件地址和名字的邮件地址类:
Address address = new InternetAddress("president@whitehouse.gov", "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("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并无提供检查邮件地址有效性的机制。固然咱们能够本身完成这个功能:验证邮件地址的字符是否按照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所要完成的高级功能。
5、使用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();
for (int i=0, n=message.length; 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
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("president@whitehouse.gov"));
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 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
Tran