文章目录
第11章 开发 JavaMail Web 应用 笔记
11.1 E-Mail 简介
邮件服务器按照提供的服务类型分为发送邮件服务器和接收邮件服务器。
发送邮件服务器使用邮件发送协,现在常用的时 SMTP 协议,所以邮件发送服务器也称为 SMTP 服务器。
接收邮件服务器使用接收邮件协议,常用的由 POP3 协议 和 IMAP 协议,所以通常邮件接收服务器称为 POP3 服务器或 IMAP 服务器。
11.1.1 SMTP 简单邮件传输协议
简单邮件传输协议(Simple Mail Transfer Protocol,SMTP),时 Internet 传送 E-Mail 的基本协议。也是 TCP/IP 协议组的成员。SMTP 协议解决邮件系统如何通过一条链路把邮件从一台机器传送到另一台机器上的问题。
SMTP 协议的优点是具有良好的可伸缩性,这也是它成功的关键,它即适用于广域网,也适用于局域网。STMP 协议由于非常简单,使得它得到了广泛的运用,在 Internet 上能够发送邮件的服务器几乎都支持 SMTP 协议。
SMTP 协议发送邮件的过程:
客户端邮件首先发送到邮件发送服务器,再由邮件发送服务器负责传送到接收方的服务器。发送邮件前,发送服务器会与接收方服务器联系,以确认接收方服务器是否已准备好接收邮件,如果已经准备好,则传送邮件;如果没有准备好发送服务器便会等待,并在一段时间后继续与接收服务器进行联系,若在规定时间内联系不上,发送服务器会发送一个消息到客户邮箱说明这一情况。这种方式在 Internet 中称为 “存储-转发” 方式,这种方式使得邮件在沿途各个网点上处于等待状态,直至允许其继续前进。虽然该方式降低了邮件的传送速度,但能极大的提高邮件到达目的地的成功率。
11.1.2 POP3 邮局协议
邮局协议第 3 版(Post Office Protocol 3, POP3),是邮件接收的基本协议,也是 TCP/IP 协议组的成员。RFC1939 描述了 POP3 协议,网址为 “http://www.ietf.org/rfc/rfc1939.txt”。
POP3 即允许以接收服务器向邮件客户发送邮件,也可以接收来自 SMTP 服务器的邮件。邮件客户端软件会与 POP3 服务器交互,下载由 POP3 服务器接收接收到邮件。基于 POP3 协议的邮件系统能提供快速、经济和方便的邮件接收服务。
基于 POP3 协议的邮件系统阅读邮件的过程:
用户通过自己所熟悉的邮件客户端软件,如 Foxmail、Outlook Express 和 MailBox等。经过相应的参数设置(主要是设置POP3 邮件服务器的 IP 地址或域名、用户名及其口令)后,只要选择接受邮件操作,就能够将远程邮件服务器上的所有邮件下载到本地硬盘上。且可以删除服务器上的邮件,有些服务器还会自动删除已经被下载的邮件,以便及时释放邮件服务器上的存储空间。可以在脱机状态下阅读本地邮件。
11.1.3 接收邮件的新协议 IMAP
IMAP(Internet Message Access Protocol)互联网消息访问协议,是一种功能比 POP3 更强大的新的邮件接收协议。目前最新的 IMAP 协议版本为 IMAP 4。
IMAP4 与 POP3 协议一样提供了方便下载的邮件服务,允许用户在脱机状态下阅读已经下载到本地硬盘的邮件。IMAP4 还
具有以下功能:
- 摘要浏览邮件的功能。 允许用户先阅读邮件的摘要信息,比如邮件的到达时间、主题、发件人和邮件的大小等,然后再做出是否下载邮件的决定。也就是说,用户不必等邮件全部下载完毕后才能知道邮件里有什么内容。如果用户根据摘要信息就可以决定某些邮件毫无用处,就可以直接在服务器上把这些邮件删除,而不必浪费宝贵的上网下载邮件时间。
- 选择性下载附件的功能。
- 鼓励用户把邮件直至存储在邮件服务器上。 用户可以在服务器上建立任意层次结构的邮件夹,并且可以灵活的在邮件夹之间移动邮件,随心所欲第管理远程服务器上的邮件夹。
- 允许用户把远程邮件服务骑上的邮箱作为信息存储工具。
11.2 JavaMail API 简介
邮件客户程序的主要任务就是向邮件服务器发送邮件以及接收来自邮件服务器的邮件。
1、javax.mail.Session 类
Session 类表示邮件会话,是 JavaMail API 的最高层入口类。Session 对象从 java.util.Properties 对象中获取配置信息。
2、javax.mail.Store 类
Store 类表示接收邮件服务器上的注册账号的存储空间,通过 Store 类的 getFolder() 方法可以访问用户的特定邮件夹。
3、javax.mail.Folder 类
Folder 类代表邮件夹,邮件都放在邮件夹中,Folder 类提供了管理邮件夹以及邮件的各种方法。
4、javax.mail.Message 类
Message 类代表电子邮件。Message 类提供了读取和设置邮件内容的方法。邮件主要包括以下内容:
- 地址信息,包括发件人地址、收件人地址列表、抄送地址列表和广播地址列表。
- 邮件标题。
- 邮件发送和接收日期。
- 邮件正文(包括纯文本和附件)。
Message 是一个个抽象类,常用的具体子类为 javax.mail.internet.MimeMessage。MimeMessage 是正文服务 MIME 协议的电子邮件。
5、javax.mail.Address 类
Address 代表邮件地址,和 Message 类一样,Address 也是个抽象类。常用的具体实现类为 javax.mail.internet.InternetAddress。
6、javax.mail.Transport 类
Transport 类根据指定的邮件发送协议(通常是 SMTP),通过指定的邮件发送服务器来发送邮件。Transport 是抽象类,它的静态方法 send(Message) 负责发送邮件。
11.3 建立 JavaMail 应用程序的开发环境
11.3.1 获取 JavaMail API 的类库。
JavaMail API 依赖于 JavaBean激活框架。
<!-- https://mvnrepository.com/artifact/javax.mail/javax.mail-api -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
<version>1.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1</version>
</dependency>
11.3.2 安装配置邮件服务器
http://ww.javathinker.net/software/merak.zip
11.4 创建 JavaMail 应用程序
(1)设置 JavaMail 属性。
String hostname = "localhost";
String username = "admin";
String password = "1234";
Properties props = new Properties();
//设置邮件发送协议,默认为 smtp
props.put("mail.transport.protocol", "smtp");
// 设置邮件接收协议
props.put("mail.store.protocol", "imap");
//指定 SMTP 协议的 Transport 具体类,运行第三方提供,默认为 com.sun.mail.smtp.SMTPTransport。
props.put("mail.smtp.class", "com.sun.mail.smtp.SMTPTransport");
// 指定支持 IMAP 协议的 Store 具体类。允许第方提供,默认为com.sun.mail.imap.IMAPStore
props.put("mail.imap.class","com.sun.mail.imap.IMAPStore");
// 指定采用 SMTP 协议的邮件发送服务器的 IP 地址或主机名
props.put("mail.smtp.host", hostname);
如果程序中希望使用上面的某些默认值,则不必调用 props.put()方法来设置。
(2)调用 javax.mail.Session 类的静态方法 Session.getDefaultInstance()获得 Session 实例,该方法根据已配置的 JavaMail 属性来创建 Session 实例。
Session mailsession= Session.getDefaultInstance(props);
(3)调用 Session 的 getStore(String protocol) 方法来获得 Store 对象。
Store store = mailsession.getStore("imap");
(4)调用 Store 对象的 connect() 方法连接到邮件接收服务器。
store.connect(hostname, username, password);
获得了 Store 对象后,就可已通过它来访问邮件服务器上的特定邮件账号了。通常会对邮件账号执行一下操作。
- 创建并发送邮件:
Message msg = new MimeMessage(mailsession);
InternetAddress[] toAddrs = InternetAddress.parse("admin@mydomain.com", false);
// 设置邮件接收者
msg.setRecipients(Message.RecipientType.TO, toAddrs);
// 设置邮件的主题
msg.setSubject("hello");
// 设置邮件的发送者
msg.setFrom(new InternetAddress("admin@mydomain.com"));
// 设置邮件正文
msg.setText("How are you");
// 发送邮件
Transport.send(msg);
Transoprt 的静态方法 send(Message) 负责发送邮件,邮件发送协议由 mail.transport.protocol 属性指定。邮件发送服务器由 mail.smtp.host 属性指定。
- 打开 inbox 邮件夹收取邮件
// 获得取名为 inbox 的邮件夹
Folder folder = store.getFolder("inbox");
// 打开邮件夹
folder.open(Folder.READ_ONLY);
//获得邮件夹中的邮件数目
System.out.println("you have " + folder.getMessageCount() + " Message in inbox");
//获得邮件夹的未读邮件数目
int unreadMessage = folder.getUnreadMessageCount();
在 IMAP 协议中,inbox 邮件夹是邮件账号的邮件夹,用户不允许删除改邮件夹,邮件服务器把所有接收到的邮件都存在改邮件夹中。
- 从邮件夹中读取邮件
// 从邮件中读取第一封邮件
Messagemsg msg = folder.getMessage(1);
//获得邮件发送者和正文主题
String from = msg.getFrom()[0];
String subject = msg.getSubject();
String text = msg.getText();
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.util.*;
public class MailClient {
protected Session session;
protected Store store;
private String sendHost="localhost"; //发送邮件服务器
private String receiveHost="localhost"; //接收邮件服务器
private String sendProtocol="smtp"; //发送邮件协议
private String receiveProtocol="imap"; //接收邮件协议
private String username = "admin";
private String password = "1234";
private String fromAddr="admin@mydomain.com"; //发送者地址
private String toAddr="admin@mydomain.·"; //接收者地址
public void init()throws Exception{
//设置属性
Properties props = new Properties();
props.put("mail.transport.protocol", sendProtocol);
props.put("mail.store.protocol", receiveProtocol);
props.put("mail.smtp.class", "com.sun.mail.smtp.SMTPTransport");
props.put("mail.imap.class", "com.sun.mail.imap.IMAPStore");
props.put("mail.smtp.host", sendHost); //设置发送邮件服务器
// 创建Session对象
session = Session.getDefaultInstance(props);
//session.setDebug(true); //输出跟踪日
// 创建Store对象
store = session.getStore(receiveProtocol);
//连接到收邮件服务器
store.connect(receiveHost,username,password);
}
public void close()throws Exception{
store.close();
}
public void sendMessage(String fromAddr,String toAddr)throws Exception{
//创建一个邮件
Message msg = createSimpleMessage(fromAddr,toAddr);
//发送邮件
Transport.send(msg);
}
public Message createSimpleMessage(String fromAddr,String toAddr)throws Exception{
//创建一封纯文本类型的邮件
Message msg = new MimeMessage(session);
InternetAddress[] toAddrs =InternetAddress.parse(toAddr, false);
msg.setRecipients(Message.RecipientType.TO, toAddrs);
msg.setSentDate(new Date());
msg.setSubject("hello");
msg.setFrom(new InternetAddress(fromAddr));
msg.setText("How are you");
return msg;
}
public void receiveMessage()throws Exception{
browseMessagesFromFolder("inbox");
}
public void browseMessagesFromFolder(String folderName)throws Exception{
Folder folder=store.getFolder(folderName);
if(folder==null)
throw new Exception(folderName+"邮件夹不存在");
browseMessagesFromFolder(folder);
}
public void browseMessagesFromFolder(Folder folder)throws Exception{
folder.open(Folder.READ_ONLY);
System.out.println("You have "+folder.getMessageCount()+" messages in inbox.");
System.out.println("You have "+folder.getUnreadMessageCount()+" unread messages in inbox.");
//读邮件
Message[] messages=folder.getMessages();
for(int i=1;i<=messages.length;i++){
System.out.println("------第"+i+"封邮件-------");
//打印邮件信息
folder.getMessage(i).writeTo(System.out);
System.out.println();
}
folder.close(false); //关闭邮件夹,但不删除邮件夹中标志为“deleted”的邮件
}
public static void main(String[] args)throws Exception {
MailClient client=new MailClient();
client.init();
client.sendMessage(client.fromAddr,client.toAddr);
client.receiveMessage();
client.close();
}
可以使用 QQ邮箱服务器作为邮件发送和接收的服务器。
“smtp.qq.com”
11.5 JavaMail Web 应用简介
向客户端提供了访问 IMAP 服务器上的邮件账号的功能,允许 Web 应用 客户管理邮件夹、查看邮件。Web 客户也可以通过特定的 SMTP 服务器发送邮件。
11.6 JavaMail 应用的程序结构
文件名称 | 描述 |
---|---|
PMessage.java | 对 Message 数据重新进行封装,提供显示邮件信息更快捷的方法 |
MailUserData.java | 用于保存客户的邮件账号信息的 JavaBean,还提供了管理邮件和邮件夹的实用方法,该JavaBean 被存放在HTTP会话范围内 |
common.jsp | 包含了各个JSP的共同内容,如 import语句和 JavaBean 的声明 |
link.jsp | 包含了 JSP 文件的共同链接 |
login.jsp | 提供客户登录页面 |
connect.jsp | 根据客户的登录信息,负责链接到接收邮件服务器上的账号 |
listallfolders.jsp | 显示客户邮件账号中的所有邮件夹 |
listonefolder.jsp | 显示客户指定的邮件夹中的所有的邮件 |
showmessage.jsp | 显示客户指定的邮件内容 |
compose.jsp | 提供创建、编辑和发送邮件的功能 |
loginout.jsp | 退出邮件系统 |
errorpage.jsp | 错误处理页面 |
web.xml | Web 应用的配置页面 |
11.6.1 重新封装 Message 数据
PMessage 类对 Message 类表示的邮件数据进行了重新封装,提供更方便的显示邮件信息的方法。如,javax.mail.Message 类中读取接受者邮件地址列表的方法为 getTo()方法,该方法返回 Address[] 类型的数组,如果要把接收邮件的地址显示到网页上,必须把 Address 数组转化为相应的字符串。
public class PMessage{
private String subject=""; //邮件标题
private String from=""; //邮件发送者地址
private String to=""; //邮件接收地址列表
private String cc=""; //邮件抄送地址列表
private String bcc=""; //邮件广播地址列表
private String date=new Date().toString(); //邮件发送或接收日期
private int size=0; //邮件大小
private String text=""; //邮件正文
private boolean readFlag; //邮件是否已读标志
public PMessage(){}
public PMessage(Message msg)throws Exception{
if(msg!=null){
SimpleDateFormat df = new SimpleDateFormat("yy.MM.dd 'at' HH:mm:ss ");
try{
date=df.format(
(msg.getSentDate()!=null) ? msg.getSentDate() : msg.getReceivedDate());
}catch(Exception e){date=new Date().toString();}
subject=msg.getSubject();
size=msg.getSize();
Object content=null;
try{
content=msg.getContent();
}catch(Exception e){}
if(msg.isMimeType("text/plain") && content!=null)
text=(String)content;
from=assembleAddress(msg.getFrom());
to=assembleAddress(msg.getRecipients(Message.RecipientType.TO));
cc=assembleAddress(msg.getRecipients(Message.RecipientType.CC));
bcc=assembleAddress(msg.getRecipients(Message.RecipientType.BCC));
}
}
public PMessage(String to,String cc,String bcc,String subj,String text){
to.replace(';',',');
cc.replace(';',',');
bcc.replace(';',',');
this.to=to;
this.cc=cc;
this.bcc=bcc;
this.subject=subj;
this.text=text;
}
//把Address数组中的邮件地址列表转换为字符串 ,邮件地址之间以逗号分割
private String assembleAddress(Address[] addr){
if(addr==null)return "";
String addrString="";
boolean tf = true;
for(int i = 0; i < addr.length; i++) {
addrString=addrString+((tf) ? " " : ", ") + getDisplayAddress(addr[i]);
tf = false;
}
return addrString;
}
//返回字符串形式的邮件地址,用于输出到网页上
private String getDisplayAddress(Address a) {
String pers = null;
String addr = null;
if(a instanceof InternetAddress &&
((pers = ((InternetAddress)a).getPersonal()) != null)) {
addr = pers + " "+"<"+((InternetAddress)a).getAddress()+">";
}else
addr = a.toString();
return addr;
}
public String getFrom(){return from;}
public void setFrom(String from){
this.from=from==null ? "" : from;
}
public String getTo(){return to;}
public void setTo(String to){
this.to=to==null ? "" : to;
}
public String getCC(){return cc;}
public void setCC(String cc){
this.cc=cc==null ? "" : cc;
}
public String getBCC(){return bcc;}
public void setBCC(String bcc){
this.bcc=bcc==null ? "" : bcc;
}
public int getSize(){return size;}
public void setSize(int size){this.size=size;}
public String getDate(){return date;}
public void setDate(String date){this.date=date;}
public String getSubject(){return subject;}
public void setSubject(String subject){
this.subject=subject==null ? "" : subject;
}
public String getText(){return text;}
public void setText(String text){
this.text=text==null ? "" : text;
}
public boolean getReadFlag(){return readFlag;}
public void setReadFlag(boolean readFlag){
this.readFlag=readFlag;
}
}
11.6.2 用于保存邮件信息账号的 JavaBean
public class MailUserData {
URLName urlName; // 客户联接邮件服务器的账号
Session session; // 客户当前使用的邮件会话
Store store; // 客户当前使用的Stroe
Folder currFolder; // 客户当前访问的 Folder
Message currMsg; //客户当前访问的 Message
public MailUserData() { }
public URLName getURLName() {
return urlName;
}
public void setURLName(URLName url){
urlName=url;
}
public Session getSession() {
return session;
}
public void setSession(Session s) {
session = s;
}
public Message getCurrMsg() {
return currMsg;
}
public void setCurrMsg(Message s) {
currMsg = s;
}
public Store getStore() {
return store;
}
public void setStore(Store s) {
store = s;
}
public Folder getCurrFolder() {
return currFolder;
}
public void setCurrFolder(Folder f) {
currFolder = f;
}
//删除用户自己创建的邮件夹,单不允许删除 Web 应用保留的邮件夹
public void doDeleteFolder(String foldername )throws Exception {
//can't delete reserved folder
if(foldername.equalsIgnoreCase("inbox")||
foldername.equalsIgnoreCase("trash")||
foldername.equalsIgnoreCase("draft")||
foldername.equalsIgnoreCase("sendbox")){
throw new Exception("Cann't delete reserved folder");
}
Folder folder=store.getFolder(foldername);
if(!folder.exists())throw new Exception("This folder does not exists");
if(folder.isOpen())folder.close(true);
folder.delete(true);
}
// 创建邮件夹
public void doCreateFolder(String foldername)throws Exception {
if(foldername==null || foldername.equals(""))
throw new Exception("You do not input new folder name when creating folder");
Folder folder=store.getFolder(foldername);
if(folder.exists())throw new Exception("This folder already exists");
folder.create(Folder.HOLDS_MESSAGES);
}
//修改用户自己创建的邮件夹名称,不允许修改Web应用保留的
public void doRenameFolder(String fromname,String toname)throws Exception {
if(toname==null || toname.equals(""))
throw new Exception("You do not input new folder name when renaming folder");
//can't rename reserved folder
if(fromname.equalsIgnoreCase("inbox")||
fromname.equalsIgnoreCase("trash")||
fromname.equalsIgnoreCase("draft")||
fromname.equalsIgnoreCase("sendbox")||
toname.equalsIgnoreCase("inbox")||
toname.equalsIgnoreCase("trash")||
toname.equalsIgnoreCase("draft")||
toname.equalsIgnoreCase("sendbox")){
throw new Exception("Cann't rename reserved folder");
}
Folder folderFrom=store.getFolder(fromname);
Folder folderTo=store.getFolder(toname);
if(!folderFrom.exists())throw new Exception("This folder does not exists");
if(folderFrom.isOpen())folderFrom.close(true);
folderFrom.renameTo(folderTo);
}
public void doDeleteMessage(int arrayOpt[],Folder f)throws Exception {
for(int i=0;i<arrayOpt.length;i++){
if(arrayOpt[i]==0)continue;
Message msg=f.getMessage(i+1);
if(!f.getName().equals("Trash")){
Message[] m=new Message[1];
m[0]=msg;
Folder Trash=store.getFolder("Trash");
f.copyMessages(m,Trash);
msg.setFlag(Flags.Flag.DELETED, true);
}else{
msg.setFlag(Flags.Flag.DELETED, true);
}
}
f.expunge();
}
public Message doAssembleMessage(PMessage msg)throws Exception {
return doAssembleMessage(msg.getTo(),msg.getCC(),msg.getBCC(),msg.getSubject(),msg.getText());
}
public Message doAssembleMessage(String to,String cc,String bcc,String subj,String text)throws Exception {
Message msg = new MimeMessage(session);
InternetAddress[] toAddrs = null, ccAddrs = null, bccAddrs=null;
if (to != null) {
toAddrs = InternetAddress.parse(to, false);
msg.setRecipients(Message.RecipientType.TO, toAddrs);
}else
throw new MessagingException("No \"To\" address specified");
if (cc != null) {
ccAddrs = InternetAddress.parse(cc, false);
msg.setRecipients(Message.RecipientType.CC, ccAddrs);
}
if (bcc != null) {
bccAddrs = InternetAddress.parse(bcc, false);
msg.setRecipients(Message.RecipientType.BCC, bccAddrs);
}
if (subj != null)
msg.setSubject(subj);
msg.setFrom(new InternetAddress(urlName.getUsername() + "@" +
urlName.getHost()));
if (text != null)
msg.setText(text);
return msg;
}
public void doSendMessage(PMessage pmsg)throws Exception {
Message msg=doAssembleMessage(pmsg);
//send message
Transport.send(msg);
//save message in sendbox folder
Folder f=store.getFolder("SendBox");
if(!f.isOpen())f.open(Folder.READ_WRITE);
doAppendMessage(msg,f);
}
public void doAppendMessage(Message msg, Folder f)throws Exception{
Message m[]=new Message[1];
m[0]=msg;
f.appendMessages(m);
}
public void doSaveMessage(PMessage pmsg)throws Exception {
Message msg=doAssembleMessage(pmsg);
//save message in draft folder
Folder f=store.getFolder("Draft");
if(!f.isOpen())f.open(Folder.READ_WRITE);
doAppendMessage(msg,f);
}
public void doMoveMessage(String toFolderName)throws Exception {
Folder folderFrom=currFolder;
Folder folderTo=store.getFolder(toFolderName);
if(!folderTo.exists())throw new Exception("Folder does not exist");
Message[] m=new Message[1];
m[0]=currMsg;
folderFrom.copyMessages(m,folderTo);
currMsg.setFlag(Flags.Flag.DELETED, true);
folderFrom.expunge();
}
}
common.jsp 包含了其他JSP页面的共同内容
<%@ page import="java.util.*" %>
<%@ page import="java.text.*" %>
<%@ page import="mypack.*" %>
<%@ page import="javax.mail.*" %>
<%@ page import="javax.mail.internet.*" %>
<%@ page import="javax.activation.*" %>
<%@ page errorPage="errorpage.jsp" %>
<jsp:useBean id="mud" scope="session" class="mypack.MailUserData"/>
<%
//检查会话是否已经失效,或者用户是否已经登入
if(mud.getStore()==null){
%>
<font color="red" size="4">
<b>Error Information:</b>
The page you visit expires or you do not login yet.
Please <a href=login.jsp>login again</a>
</font>
<%
return; }
%>
所有的 JSP 页面都包含了一下超链接:
- CheckMail.jsp :查看收件箱中的邮件
- Folders:管理邮件夹。
- Compose:创建和编辑邮件
- Logout:退出邮件系统
link.jsp
<a href="listonefolder.jsp?folder=inbox" >CheckMail</a>
<a href="listallfolders.jsp" >Folders</a>
<a href="compose.jsp" >Compose</a>
<a href="logout.jsp">Logout</a>
<hr>
11.6.4 登陆 IMAP 服务器上的账号
login.jsp
<html><head><title>JavaMail</title></head>
<body >
<p><center>
<font size=+3><b>Welcome to Java Mail Web</b></font>
</center></p><hr>
<%
String loginfail=(String)request.getAttribute("loginfail");
if(loginfail!=null && loginfail.equals("true")){
%>
<center>
<p><font color="red">
Login Failed. MailServer,UserName or password are incorrect.
</font></p>
</center>
<% } %>
<form action="connect.jsp" method="post" >
<center>
<table>
<tr>
<td>IMAP Mail Server:</td>
<td><input type="text" name="hostname" size="25"></td>
</tr>
<tr>
<td>Username:</td>
<td><input type="text" name="username" size="25"></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password" size="25"></td>
</tr>
</table>
</center>
<center><br>
<input type="submit" value="Login">
<input type="reset" name="Reset" value="Reset">
</center>
</form>
</body></html>
提交了登陆表单后,请求由 connect.jsp 来处理,根据客户的登陆信息链接到 IMAP 邮件服务器上的邮件账号。
conect.jsp
<%@ page import="javax.mail.*" %>
<%@ page import="javax.mail.internet.*" %>
<%@ page import="javax.activation.*" %>
<%@ page import="java.util.*" %>
<%@ page errorPage="errorpage.jsp" %>
<jsp:useBean id="mud" scope="session" class="mypack.MailUserData"/>
<%!
private Properties props=null;
public void jspInit() {
props = System.getProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.store.protocol", "imap");
props.put("mail.smtp.class", "com.sun.mail.smtp.SMTPTransport");
props.put("mail.imap.class", "com.sun.mail.imap.IMAPStore");
props.put("mail.smtp.host", "localhost");
}
%>
<%
String hostname = request.getParameter("hostname");
String username = request.getParameter("username");
String password = request.getParameter("password");
//创建Mail Session对象
Session mailsession = Session.getDefaultInstance(props, null);
//创建Store对象
Store store = mailsession.getStore("imap");
try{
//连接邮件服务器
store.connect(hostname,username, password);
}catch(Exception e){
request.setAttribute("loginfail","true");
%>
<jsp:forward page="login.jsp" />
<%
}
%>
<%
// 在MailUserData对象中保存Mail Session对象和Store对象
mud.setSession(mailsession);
mud.setStore(store);
//创建并打开默认的Trash、Draft和sendbox邮件夹
Folder folder=store.getFolder("Trash");
if(!folder.exists())folder.create(Folder.HOLDS_MESSAGES);
folder=store.getFolder("SendBox");
if(!folder.exists())folder.create(Folder.HOLDS_MESSAGES);
folder=store.getFolder("Draft");
if(!folder.exists())folder.create(Folder.HOLDS_MESSAGES);
folder.open(Folder.READ_WRITE);
//把URL保存到MailUserData对象中
URLName url = new URLName("imap",hostname, -1, "inbox", username, password);
mud.setURLName(url);
%>
<jsp:forward page="listallfolders.jsp" />
如果邮件链接服务器失败,会把请求转发到登陆页面。链接成功则转发到邮件夹管理页面。
11.6.5 管理邮件夹
listallfolders.jsp 用于显示用户邮件夹信息,并提供了删除和创建邮件夹,以及修改邮件夹名称的功能。
listallfolders.jsp
<%@ include file="common.jsp" %>
<html>
<head><title>listallfolders</title></head>
<body>
<center><font size="+3"><b>Folder List</b></font></center>
<p>
<%@ include file="link.jsp" %>
<%
String operation=request.getParameter("operation");
String folderName=request.getParameter("folder");
String newFolderName=request.getParameter("newFolderName");
String error=null;
try{
if(operation!=null && operation.equals("create"))
mud.doCreateFolder(newFolderName);
if(operation!=null && operation.equals("delete"))
mud.doDeleteFolder(folderName);
if(operation!=null && operation.equals("rename"))
mud.doRenameFolder(folderName,newFolderName);
}catch(Exception e){
out.println("<font color='red'>"+e.getMessage()+"</font><p>");
}
%>
<%
Folder folder=null;
Store store=mud.getStore();
folder = store.getDefaultFolder();
if (folder == null)
throw new MessagingException("No folder is available");
Folder[] f = folder.list("%");
%>
<table width="75%" border=1 align=left>
<tr bgcolor="#FFCC66">
<td rowspan=1 width="25%" ><b>FolderName</b></td>
<td rowspan=1 width="25%" ><b>Total Messages</b></td>
<td rowspan=1 width="25%" ><b>Unread Messages</b></td>
</tr>
<% for(int i=0; i<f.length;i++){ %>
<tr valigh=middle bgcolor="#FFFFCC">
<td>
<a href="listonefolder.jsp?folder=<%=f[i].getName()%>">
<%=f[i].getName()%></a>
</td>
<td><%=f[i].getMessageCount()%></td>
<td><%=f[i].getUnreadMessageCount()%></td>
</tr>
<% } %>
<tr>
<td colspan=3>
<table width="50%" border=0 align=center>
<tr><td colspan=2 align=center><b><br>Folder Operation</b></td><tr>
<form action="listallfolders.jsp" >
<tr>
<td>select operation:</td>
<td>
<select name="operation">
<option value="create" selected> create folder
<option value="delete" >delete folder
<option value="rename" >rename folder
</select>
</td>
</tr>
<tr>
<td>select folder:</td>
<td>
<select name="folder">
<%
for(int i=0;i<f.length;i++){
if(!f[i].getName().equalsIgnoreCase("inbox")
&& !f[i].getName().equalsIgnoreCase("Draft")
&& !f[i].getName().equalsIgnoreCase("Trash")
&& !f[i].getName().equalsIgnoreCase("SendBox")){
if(i==0)
out.println("<OPTION VALUE=\""+f[i].getName()+" selected\">"+f[i].getName());
else
out.println("<OPTION VALUE=\""+f[i].getName()+" \">"+f[i].getName());
}
}
%>
</select></td></tr>
<tr>
<td>new folder name:</td>
<td><input type="text" name="newFolderName"></td>
</tr>
<tr>
<td colspan=2 align=center>
<input type="submit" name="submit" value="submit">
</td>
</tr>
</form>
</table>
</td>
</tr>
</table>
</body></html>
11.6.6 查看邮件夹中的邮件信息
listonefolder.jsp 用于显示用户指定的邮件夹中的邮件信息。
listonefolder.jsp
<%@ include file="common.jsp" %>
<html>
<head><title>listonefolder</title></head>
<body>
<%
String folderName=request.getParameter("folder");
SimpleDateFormat df = new SimpleDateFormat("yy.MM.dd 'at' HH:mm:ss ");
Folder f=null;
if(folderName!=null){
f=mud.getStore().getFolder(folderName);
mud.setCurrFolder(f);
}else{
f=mud.getCurrFolder();
folderName=f.getName();
}
if(!f.isOpen())f.open(Folder.READ_WRITE);
int msgCount = f.getMessageCount();
int unReadCount = f.getUnreadMessageCount();
//删除邮件
int arrayOpt[]=new int[msgCount];
for(int i=1;i<=msgCount;i++){
String optS=request.getParameter("delIndex"+i);
if(optS!=null) arrayOpt[i-1]=1;
}
mud.doDeleteMessage(arrayOpt,f);
//刷新邮件总数以及未读邮件总数
if(f.isOpen())f.close(true);
f.open(Folder.READ_WRITE);
msgCount = f.getMessageCount();
unReadCount = f.getUnreadMessageCount();
%>
<center>
<font size="+3"><b>CurrentFolder:<%=folderName%></b></font>
</center><p>
<%@ include file="link.jsp" %>
<b>Total Messages:<%=msgCount%></b>
<b>Unread Messages:<%=unReadCount%></b>
<form action="listonefolder.jsp">
<table cellpadding=1 cellspacing=1 width="100%" border=1>
<tr bgcolor="ffffcc">
<td width="5%" ></td>
<td width="35%" ><b>Sender</b></td>
<td width="20%" ><b>Date</b></td>
<td width="30%" ><b>Subject</b></td>
<td width="10%" ><b>Size</b></td>
</tr>
<%
Message m = null;
//显示每封邮件的头信息
for (int i = 1; i <= msgCount; i++) {
m = f.getMessage(i);
//如果邮件设了DELETED标志,就不用显示
if (m.isSet(Flags.Flag.DELETED))
continue;
%>
<tr valign=middle >
<%--opt --%>
<td width=5% ><input type=checkbox name="delIndex<%=i%>"></td>
<%-- from --%>
<td width="35%">
<% if(!m.isSet(Flags.Flag.SEEN)) out.print("<b>"); %>
<% out.println((m.getFrom() != null) ? m.getFrom()[0].toString() : " "); %>
<% if(!m.isSet(Flags.Flag.SEEN))out.print("</b>"); %>
</td>
<%--date --%>
<td width="20%">
<%
if(!m.isSet(Flags.Flag.SEEN))out.println("<b>");
out.println(
df.format((m.getSentDate()!=null) ? m.getSentDate() : m.getReceivedDate()));
if(!m.isSet(Flags.Flag.SEEN))out.println("</b>");
%>
</td>
<%--subject & link --%>
<td width="30%">
<%
String link="";
if(f.getName().equals("Draft")){
link="compose.jsp?edit=true";
mud.setCurrMsg(m);
}else
link="showmessage.jsp" + "?messageindex=" + i;
if(!m.isSet(Flags.Flag.SEEN))out.println("<b>");
out.println("<a href="+link+">" +
((m.getSubject() != null)&& !m.getSubject().equals("") ?
m.getSubject() : "<i>No Subject</i></a>"));
if(!m.isSet(Flags.Flag.SEEN))out.println("</b>");
%>
</td>
<%-- size--%>
<td width="10%">
<%
if(!m.isSet(Flags.Flag.SEEN))out.println("<b>");
out.println(m.getSize()+"Bytes");
if(!m.isSet(Flags.Flag.SEEN))out.println("</b>");
%>
</td>
</tr>
<% } %>
</table>
<p><input type="submit" name="submit" value="delete messages"></p></form>
</body></html>
11.6.7 查看邮件内容
showmessage.jsp 用于显示邮件的内容
showmessage.jsp
<%@ include file="common.jsp" %>
<html>
<head><title>show message</title></head>
<body >
<%
Folder folder=mud.getCurrFolder();
Message currmsg=null;
int msgNum=1;
String messageindex=request.getParameter("messageindex");
if(messageindex!=null){
msgNum=Integer.parseInt(messageindex);
currmsg=folder.getMessage(msgNum);
mud.setCurrMsg(currmsg);
}else
currmsg=mud.getCurrMsg();
PMessage displayMsg=new PMessage(currmsg);
%>
<center><font size="+3"><b>
<%
out.println("Message in "+folder.getName()+" folder ");
%>
</b></font></center><p>
<%@ include file="link.jsp" %>
<a href="compose.jsp?reply=true" >Reply</a>
<a href="listonefolder.jsp?delIndex<%=msgNum%>=on" >Delete</a>
<%-- 显示邮件信息--%>
<table width=90%>
<tr>
<td>
<b>Date:</b> <%=displayMsg.getDate()%><br>
<b>From:</b> <%=displayMsg.getFrom()%><br>
<b>To:</b> <%=displayMsg.getTo()%><br>
<b>CC:</b> <%=displayMsg.getCC()%><br>
<b>Subject:</b> <%=displayMsg.getSubject()%><br>
<pre><%=displayMsg.getText()%></pre>
</td>
</tr>
</table>
</body></html>
11.6.8 创建和发送邮件
commpose.jsp 提供了编辑邮件的表单。
<%@ include file="common.jsp" %>
<html>
<head><title>composemessage</title></head>
<body>
<center><font size="+3"><b>Compose Message</b></font></center><p>
<%@ include file="link.jsp" %>
<%
String operation=request.getParameter("operation");
String reply=request.getParameter("reply");
String edit=request.getParameter("edit");
String to = request.getParameter("to");
String cc = request.getParameter("cc");
String bcc = request.getParameter("bcc");
String subj = request.getParameter("subject");
String text = request.getParameter("text");
PMessage displayMsg=new PMessage();
//发送邮件
if(operation != null && operation.equals("send")) {
displayMsg=new PMessage(to, cc, bcc, subj,text);
mud.doSendMessage(displayMsg);
out.println("Message is sent to "+to);
}
//保存邮件
if(operation != null && operation.equals("save")) {
displayMsg=new PMessage(to, cc, bcc, subj,text);
mud.doSaveMessage(displayMsg);
out.println("Message is saved to Draft");
}
//获得回复邮件的初稿
if(reply!=null){
Message currmsg=mud.getCurrMsg();
displayMsg=new PMessage(currmsg.reply(true));
}
//编辑草稿邮件
if(edit!=null) {
displayMsg=new PMessage(mud.getCurrMsg());
}
%>
<form action="compose.jsp" method="post">
<table border="0" width="100%">
<tr>
<td width="16%" height="22"><p align="right"><b>to:</b></td>
<td width="84%" height="22">
<input type="text" name="to" value="<%=displayMsg.getTo()%>" size="30" >
</td>
</tr>
<tr>
<td width="16%"><p align="right"><b>cc:</b></td>
<td width="84%">
<input type="text" name="cc" value="<%=displayMsg.getCC()%>" size="30">
</td>
</tr>
<tr>
<td width="16%"><p align="right"><b>bcc:</b></td>
<td width="84%">
<input type="text" name="bcc" value="<%=displayMsg.getBCC()%>" size="30">
</td>
</tr>
<tr>
<td width="16%"><p align="right"><b>subject:</b></td>
<td width="84%">
<input type="text" name="subject" value="<%=displayMsg.getSubject()%>" size="30">
</td>
</tr>
<tr>
<td width="16%"> </td>
<td width="84%">
<textarea name="text" rows="5" cols="40"> <%=displayMsg.getText()%></textarea>
</td>
</tr>
</table>
<center>
<b>
<input type="radio" name="operation" value="save">save draft
<input type="radio" name="operation" value="send" checked>send
</b>
<input type="submit" name="submit" value="submit">
<input type="reset" name="reset" value="reset">
</center>
</form>
</body></html>
11.6.9 退出邮件系统
loginout.jsp
<%@ include file="common.jsp" %>
<%
String username=mud.getURLName().getUsername();
mud.getStore().close(); //断开与邮件服务器的连接
session.invalidate(); //结束HTTP会话
%>
<html>
<head><title>Logout</title></head>
<body>
<h3>Goodbye,<%=username%>!</h3><br>
<strong><a href="login.jsp">Login again</a></strong>
</body></html>
11.7 在 Tomcat 中配置邮件会话(Mail Session)
可以把 Mail Session 作为一种 JNDI 资源在 Tomcat 中配置,Tomcat 提供了创建 Mail Session 对象的工厂:org.apache.naming.factory.MailSessionFactory。
11.7.1 在 context.xml 中配置 Mail Session 资源
<Context reloadable="true" >
<!--name JNDI 资源名-->
<!--auth:指定管理器,Container 容器, application 应用-->
<Resource name="mail/session"
auth="Container"
type="javax.mail.Session"
mail.smtp.host="localhost"
mail.store.protocol="imap" />
</Context>
11.7.2 在 web.xml 中加入对 JNDI 资源的引用
<resource-ref>
<description>Java Mail Session</description>
<res-ref-name>mail/session</res-ref-name>
<res-type>javax.mail.Session</res-type>
<res-auth>Container</res-auth>
</resource-ref>
11.7.3 在 JavaMail 应用中获取 JNDI Mail Session 资源
Context ctx = new InitialContext();
if(ctx == null )
throw new Exception("No Context");
Session mailsession =(Session)ctx.lookup("java:comp/env/mail/session");
//获得Store对象
Store store = mailsession.getStore("imap");