javaMail笔记.docx
《javaMail笔记.docx》由会员分享,可在线阅读,更多相关《javaMail笔记.docx(24页珍藏版)》请在冰点文库上搜索。
javaMail笔记
JavaMail API简介
SMTP
简单邮件传输协议(SimpleMailTransferProtocol,SMTP)由RFC821定义。
它定义了发送电子邮件的机制。
在JavaMailAPI环境中,您基于JavaMail的程序将和您的公司或因特网服务供应商的(InternetServiceProvider's,ISP's)SMTP服务器通信。
SMTP服务器会中转消息给接收方SMTP服务器以便最终让用户经由POP或IMAP获得。
这不是要求SMTP服务器成为开放的中继,尽管SMTP服务器支持身份验证,不过还是得确保它的配置正确。
像配置服务器来中继消息或添加删除邮件账号这类任务的实现,JavaMailAPI中并不支持。
POP
POP代表邮局协议(PostOfficeProtocol)。
目前用的是版本3,也称POP3,RFC1939定义了这个协议。
POP是一种机制,因特网上大多数人用它得到邮件。
它规定每个用户一个邮箱的支持。
这就是它所能做的,而这也造成了许多混淆。
使用POP时,用户熟悉的许多性能并不是由POP协议支持的,如查看有几封新邮件消息这一性能。
这些性能内建于如Eudora或MicrosoftOutlook之类的程序中,它们能记住一些事,诸如最近一次收到的邮件,还能计算出有多少是新的。
所以当使用JavaMailAPI时,如果您想要这类信息,您就必须自己算。
IMAP
IMAP是更高级的用于接收消息的协议。
在RFC2060中被定义,IMAP代表因特网消息访问协议(InternetMessageAccessProtocol),目前用的是版本4,也称IMAP4。
在用到IMAP时,邮件服务器必需支持这个协议。
不能仅仅把使用POP的程序用于IMAP,并指望它支持IMAP所有性能。
假设邮件服务器支持IMAP,基于JavaMail的程序可以利用这种情况―用户在服务器上有多个文件夹(folder),并且这些文件夹可以被多个用户共享。
因为有这一更高级的性能,您也许会认为所有用户都会使用IMAP。
事实并不是这样。
要求服务器接收新消息,在用户请求时发送到用户手中,还要在每个用户的多个文件夹中维护消息。
这样虽然能将消息集中备份,但随着用户长期的邮件夹越来越大,到磁盘空间耗尽时,每个用户都会受到损失。
使用POP,就能卸载邮件服务器上保存的消息了。
NNTP
因为JavaMailAPI将供应商和所有其它的东西分开了,您就能轻松添加额外的协议支持。
Sun保留了一张第三方供应商列表,他们利用了Sun不提供超出(out-of-the-box)支持范围的协议。
您会找到NNTP(网络新闻传输协议)[新闻组]、S/MIME(安全多用途因特网邮件扩展)及其它支持。
MIME
MIME代表多用途因特网邮件扩展标准(MultipurposeInternetMailExtensions)。
它不是邮件传输协议。
但对传输内容的消息、附件及其它的内容定义了格式。
这里有很多不同的有效文档:
RFC822、RFC2045、RFC2046和RFC2047。
作为一个JavaMailAPI的用户,您通常不必对这些格式操心。
无论如何,一定存在这些格式而且程序会用到它。
JavaMailAPI的核心类:
Session、Message、Address、Authenticator、Transport、Store和Folder
Session对象
Session类定义了一个基本邮件会话(session)。
所有其它类都是经由这个session才得以生效。
Session对象用java.util.Properties对象获取信息,如邮件服务器、用户名、密码及整个应用程序中共享的其它信息。
类的构造器是私有的(private)。
您可以得到单个缺省session,它能用getDefaultInstance()方法被共享:
Propertiesprops=newProperties();
//fillpropswithanyinformation
Sessionsession=Session.getDefaultInstance(props,null);
或者,您还可以用getInstance()创建一个独立的session:
Propertiesprops=newProperties();
//fillpropswithanyinformation
Sessionsession=Session.getDefaultInstance(props,null);
对于这两种情况,null参数都是Authenticator对象(在这次没有使用)。
对于大多数情况,共享的session已经够用了,即使要处理多个用户邮箱的邮件session也一样。
您可以在通信过程中稍后的步骤加入用户名和密码组合,让一切保持独立。
Message对象
一旦获得Session对象,就可以继续创建要发送的消息。
这由Message类来完成。
因为Message是个抽象类,您必需用一个子类,多数情况下为javax.mail.internet.MimeMessage。
MimeMessage是个能理解MIME类型和头的电子邮件消息,正如不同RFC中所定义的。
虽然在某些头部域非ASCII字符也能被译码,但Message头只能被限制为用US-ASCII字符。
要创建一个Message,请将Session对象传递给MimeMessage构造器:
MimeMessagemessage=newMimeMessage(session);
注意:
还存在其它构造器,如用按RFC822格式的输入流来创建消息。
一旦获得消息,您就可以设置各个部分,因为Message实现Part接口(且MimeMessage实现MimePart)。
设置内容的基本机制是setContent()方法,同时使用参数,分别代表内容和mime类型:
message.setContent("Hello","text/plain");
但如果,您知道您在使用MimeMessage,而且消息是纯文本格式,您就可以用setText()方法,它只需要代表实际内容的参数,(MIME类型缺省为text/plain):
message.setText("Hello");//注意不要采用这表示1个信封应该使用上面的方法
后一种格式是设置纯文本消息内容的首选机制。
至于发送其它类型的消息,如HTML文件格式的消息,我们首选前者。
用setSubject()方法设置subject(主题):
message.setSubject("First");
Address对象
一旦您创建了Session和Message,并将内容填入消息后,就可以用Address确定信件地址了。
和Message一样,Address也是个抽象类。
您用的是javax.mail.internet.InternetAddress类。
若创建的地址只包含电子邮件地址,只要传递电子邮件地址到构造器就行了。
Addressaddress=newInternetAddress("president@whitehouse.gov");
若希望名字紧挨着电子邮件显示,也可以把它传递给构造器:
Addressaddress=newInternetAddress("president@whitehouse.gov","GeorgeBush");
需要为消息的from域和to域创建地址对象。
除非邮件服务器阻止,没什么能阻止你发送一段看上去是来自任何人的消息。
一旦创建了address(地址),将它们与消息连接的方法有两种。
如果要识别发件人,您可以用setFrom()和setReplyTo()方法。
message.setFrom(address)
需要消息显示多个from地址,可以使用addFrom()方法:
Addressaddress[]=...;
message.addFrom(address);
若要识别消息recipient(收件人),您可以使用addRecipient()方法。
除address(地址)外,这一方法还请求一个Message.RecipientType。
message.addRecipient(type,address)
三种预定义的地址类型是:
Message.RecipientType.TO主要发送
Message.RecipientType.CC抄送
Message.RecipientType.BCC匿名发送
如果消息是发给副总统的,同时发送一个副本(carboncopy)给总统夫人,以下做法比较恰当:
AddresstoAddress=newInternetAddress("vice.president@whitehouse.gov");
AddressccAddress=newInternetAddress("first.lady@whitehouse.gov");
message.addRecipient(Message.RecipientType.TO,toAddress);
message.addRecipient(Message.RecipientType.CC,ccAddress);
JavaMailAPI没有提供电子邮件地址有效性核查机制。
虽然通过编程,自己能够扫描有效字符(如RFC822中定义的)或验证邮件交换(mailexchange,MX)记录,但这些功能不属于JavaMailAPI。
Authenticator对象验证作用
与类一样,JavaMailAPI也可以利用Authenticator通过用户名和密码访问受保护的资源。
对于JavaMailAPI来说,这些资源就是邮件服务器。
JavaMailAuthenticator在javax.mail包中,而且它和中同名的类Authenticator不同。
两者并不共享同一个Authenticator,因为JavaMailAPI用于Java1.1,它没有类别。
要使用Authenticator,先创建一个抽象类的子类,并从getPasswordAuthentication()方法中返回PasswordAuthentication实例。
创建完成后,您必需向session注册Authenticator。
然后,在需要认证的时候,就会通知Authenticator。
您可以弹出窗口,也可以从配置文件中(虽然没有加密是不安全的)读取用户名和密码,将它们作为PasswordAuthentication对象返回给调用程序。
Propertiesprops=newProperties();
//fillpropswithanyinformation
Authenticatorauth=newMyAuthenticator();
Sessionsession=Session.getDefaultInstance(props,auth);
Transport发送对象
消息发送的最后一部分是使用Transport类。
这个类用协议指定的语言发送消息(通常是SMTP)。
它是抽象类,它的工作方式与Session有些类似。
仅调用静态send()方法,就能使用类的缺省版本:
Transport.send(message);
或者,您也可以从针对您的协议的会话中获得一个特定的实例,传递用户名和密码(如果不必要就不传),发送消息,然后关闭连接。
message.saveChanges();//implicitwithsend()
Transporttransport=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使用什么协议:
//Storestore=session.getStore("imap");
Storestore=session.getStore("pop3");
store.connect(host,username,password);
连接到Store之后,接下来,您就可以获取一个Folder,您必需先打开它,然后才能读里面的消息。
Folderfolder=store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Messagemessage[]=folder.getMessages();
POP3唯一可以用的文件夹是INBOX。
如果使用IMAP,还可以用其它文件夹。
注意:
Sun的供应商有意变得聪明。
虽然Messagemessage[]=folder.getMessages();看上去是个很慢的操作,它从服务器上读取每一条消息,但仅在你实际需要消息的一部分时,消息的内容才会被检索。
一旦有了要读的Message,您可以用getContent()来获取其内容,或者用writeTo()将内容写入流。
getContent()方法只能得到消息内容,而writeTo()的输出却包含消息头。
System.out.println(((MimeMessage)message).getContent());
一旦读完邮件,要关闭与folder和store的连接。
folder.close(aBoolean);
store.close();
传递给folder的close()方法的boolean表示是否清除已删除的消息从而更新folder。
案例说明
发送消息
发送电子邮件消息这一过程包括获取一个会话,创建并填充一则消息,然后发送。
得到Session时,经由设置传递的Properties对象中的mail.smtp.host属性,可以指定您的SMTP服务器:
Stringhost=...;
Stringfrom=...;
Stringto=...;
//Getsystemproperties
Propertiesprops=System.getProperties();
//Setupmailserver
props.put("mail.smtp.host",host);
//Getsession
Sessionsession=Session.getDefaultInstance(props,null);
//Definemessage
MimeMessagemessage=newMimeMessage(session);
message.setFrom(newInternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
newInternetAddress(to));
message.setSubject("HelloJavaMail");
message.setText("WelcometoJavaMail");
//Sendmessage
Transport.send(message);
您应该将代码放在一个try-catch程序块中,这样创建和发送消息时就能够抛出异常。
消息的提取
为读邮件,您获取一个会话,获取并连接一个用于邮箱的适宜的存储(store),打开适宜的文件夹,然后获取您的消息。
同样,切记完成后关闭连接。
Stringhost=...;
Stringusername=...;
Stringpassword=...;
//Createemptyproperties
Propertiesprops=newProperties();
//Getsession
Sessionsession=Session.getDefaultInstance(props,null);
//Getthestore
Storestore=session.getStore("pop3");
store.connect(host,username,password);
//Getfolder
Folderfolder=store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
//Getdirectory
Messagemessage[]=folder.getMessages();
for(inti=0,n=message.length;iSystem.out.println(i+":
"+message[i].getFrom()[0]
+"\t"+message[i].getSubject());
}
//Closeconnection
folder.close(false);
store.close();
对每条消息做些什么由您决定。
上面的代码块只是显示这些消息的发件人和主题。
技术上讲,from地址列表可能为空,而getFrom()[0]调用会抛出一个异常。
要显示全部信息,您可以在用户看完from和subject域之后给出提示,如用户有需要,就调用消息的writeTo()方法来实现。
BufferedReaderreader=newBufferedReader(
newInputStreamReader(System.in));
//Getdirectory
Messagemessage[]=folder.getMessages();
for(inti=0,n=message.length;iSystem.out.println(i+":
"+message[i].getFrom()[0]
+"\t"+message[i].getSubject());
System.out.println("Doyouwanttoreadmessage?
"+
"[YEStoread/QUITtoend]");
Stringline=reader.readLine();
if("YES".equals(line)){
message[i].writeTo(System.out);
}elseif("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提出要求。
要删除消息,您可以设置消息的DELETEDflag:
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实例,如下所示:
//Setupproperties
Propertiesprops=System.getProperties();
props.put("mail.pop3.host",host);
//Setupauthentication,getsession
Authenticatorauth=newPopupAuthenticator();
Sessionsession=Session.getDefaultInstance(props,auth);
//Getthestore
Storestore=session.getStore("pop3");
store.connect();
然后,您创建一个Authenticator子类并从getPasswordAuthentication()