1、JavaMail概述:html
JavaMail是由Sun定义的一套收发电子邮件的API,不一样的厂商能够提供本身的实现类。但它并无包含在JDK中,而是做为JavaEE的一部分。java
厂商所提供的JavaMail服务程序能够有选择地实现某些邮件协议,常见的邮件协议包括:spring
l SMTP:简单邮件传输协议,用于发送电子邮件的传输协议;编程
l POP3:用于接收电子邮件的标准协议;安全
l IMAP:互联网消息协议,是POP3的替代协议。服务器
这三种协议都有对应SSL加密传输的协议,分别是SMTPS,POP3S和IMAPS。网络
除JavaMail服务提供程序以外,JavaMail还须要JAF(JavaBeans Activation Framework)来处理不是纯文本的邮件内容,这包括MIME(多用途互联网邮件扩展)、URL页面和文件附件等内容。下图描述了JavaMail的体系结构。session
mail.jar:此JAR文件包含JavaMail API和Sun提供的SMTP、IMAP和POP3服务提供程序;并发
activation.jar:此JAR文件包含JAF API和Sun的实现。socket
2、对相关协议的回顾:
一、介绍
在研究 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
POP 表明邮局协议(Post Office Protocol)。目前用的是版本 3,也称 POP3,RFC 1939 定义了这个协议。POP 是一种机制,因特网上大多数人用它获得邮件。它规定每一个用户一个邮箱的支持。这就是它所能作的,而这也形成了许多混淆。使用POP 时,用户熟悉的许多性能并非由 POP 协议支持的,如查看有几封新邮件消息这一性能。这些性能内建于如 Eudora 或Microsoft Outlook 之类的程序中,它们能记住一些事,诸如最近一次收到的邮件,还能计算出有多少是新的。因此当使用JavaMail API 时,若是您想要这类信息,您就必须本身算。
四、IMAP
IMAP 是更高级的用于接收消息的协议。在 RFC 2060 中被定义,IMAP 表明因特网消息访问协议(Internet Message Access Protocol),目前用的是版本 4,也称 IMAP4。在用到 IMAP 时,邮件服务器必需支持这个协议。不能仅仅把使用POP 的程序用于 IMAP,并期望它支持 IMAP 全部性能。假设邮件服务器支持 IMAP,基于 JavaMail 的程序能够利用这种状况 — 用户在服务器上有多个文件夹(folder),而且这些文件夹能够被多个用户共享。
由于有这一更高级的性能,您也许会认为全部用户都会使用 IMAP。事实并非这样。要求服务器接收新消息,在用户请求时发送到用户手中,还要在每一个用户的多个文件夹中维护消息。这样虽然能将消息集中备份,但随着用户长期的邮件夹愈来愈大,到磁盘空间耗尽时,每一个用户都会受到损失。使用 POP,就能卸载邮件服务器上保存的消息了。
五、MIME
MIME 表明多用途因特网邮件扩展标准(Multipurpose Internet Mail Extensions)。它不是邮件传输协议。但对传输内容的消息、附件及其它的内容定义了格式。这里有不少不一样的有效文档:RFC 82二、RFC 204五、RFC 2046 和 RFC 2047。做为一个 JavaMail API 的用户,您一般没必要对这些格式操心。不管如何,必定存在这些格式并且程序会用到它。
六、NNTP及其余
由于 JavaMail API 将供应商和全部其它的东西分开了,您就能轻松添加额外的协议支持。Sun 保留了一张第三方供应商列表,他们利用了 Sun 不提供超出(out-of-the-box)支持范围的协议。您会找到 NNTP(网络新闻传输协议)[新闻组]、S/MIME(安全多用途因特网邮件扩展)及其它支持。
3、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("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 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("president@whitehouse.gov"));
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