Java Web之开发JavaMail Web应用
write:2022-5-18
前文学习了Java Web访问数据库的方法:Java Web之访问数据库,本文介绍Java Mail Web应用的开发方法,这个应用实际上是邮件服务器的客户端程序,它是基于浏览器的客户端程序,用户通过这个平台可以登录到邮件服务器,然后进行收发邮件以及管理邮件的操作;
文章目录
1. E-mail协议简介
1)发送邮件服务器:使用SMTP协议
2)接收邮件服务器 :使用POP3或IMAP协议
客户机A向客户机B发送邮件的过程:
1.1 SMTP简单邮件传送协议
SMTP协议:Simple Mail Transfer Protocol,即简单邮件传输协议,是Internet传送E-mail的基本协议,也是TCP/IP协议组的成员。SMTP协议解决E-mail系统如何通过一条链路,把邮件从一台机器传递到另一台机器上的问题。
1.2 POP3邮局协议
POP3(Post Office Protocol 3)即邮局协议第3版,这是 Internet接收E-mail的基本协议,也是TCP/IP协议组的成员。
基于POP3协议的E-mail系统阅读信件的过程如下:
用户通过自己所熟悉的E-mail客户端软件,例如Foxmail、Outlook Express和the Bat等,经过相应的参数设置(主要是设置POP3 邮件服务器的IP地址或者域名、用户帐号及其对应密码)后,只要选择接收邮件操作,就能够将所有的邮件从远端的邮件服务器下载到用户的本地硬盘里。邮件下载之后,用户就可以在本地阅读了。当然,用户如果想节省上网费用,也可以选择在脱机状态下慢慢地阅读邮件。
1.3 E-mail接收的新协议IMAP
IMAP(Internet Message Access Protocol),互联网消息访问协议,即直接收取邮件的协议,是与POP3不同的一种E-mail接收的新协议,比POP3协议的功能更加强大。
目前最新的版本为IMAP4。
IMAP4与POP3协议一样提供了方便的下载邮件服务,允许用户在脱机状态下阅读已经下载到本地硬盘的邮件。
但IMAP4的功能远远不只这些,它还具有以下功能:
(1)摘要浏览邮件的功能(用户可以看到摘要信息,再决定是否需要下载,如果不需要,甚至可以在服务器端就把它删除)。
(2)选择性下载附件的功能。
(3)鼓励用户把邮件一直存储在邮件服务器上(方便用户可以在不同软件上通过IMAP协议操作同一服务器端的邮件)。
(4)允许用户把远程邮件服务器上的邮箱作为信息存储工具。
2. Java Mail API简介
现在明白了发送接收邮件是以下过程:
其中的邮件客户程序通常是一些桌面软件,例如Foxmail、Outlook Express等,采用C/S(Client/Server)结构,但我们可以试一试用java语言编写一个邮件客户程序,需要借助Java Mail API:
Java Mail API支持各种电子邮件通信协议,如IMAP、POP3和SMTP,为Java应用程序提供了电子邮件处理的通用接口。
javax.mail.Session:代表邮件会话
javax.mail.Store:代表接收邮件服务器上的注册账号的存储空间
javax.mail.Folder:代表邮件夹
javax.mail.Message:代表电子邮件
javax.mail.Address:邮件地址
javax.mail.Transport:负责发送邮件
2.1 Java Mail API的最主要的类
(1)javax.mail.Session:Session 类定义了一个基本邮件会话,是Java Mail 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 类型电子邮件消息。MIME(Multipurpose Internet Mail Extensions)是一种电子邮件编码方式,它可以将发信人的电子邮件中的文本以及各种附件都打包后发送,传送时即时编码,收信人的软件收到邮件后也是即时解码还原,完全自动化,非常方便。当然先决条件是双方的软件都必须支持MIME编码,要不然发信人很方便地把信送出去了,但收信人的软件如果没有这种功能,无法把它还原,看到的也就是一大堆乱码了。
(5)javax.mail.Address:Address代表了邮件地址,和 Message 一样,Address 也是个抽象类。常用的子类为 javax.mail.internet.InternetAddress 类。
(6)javax.mail.Transport:Transport 类根据指定的邮件发送协议(通常是 SMTP),通过指定的邮件发送服务器来发送邮件。Transport 类是抽象类,它提供了一个静态方法send(Message)来发送邮件。
2.2 获得JavaMail JAR文件
开发JavaMail应用程序需要两个JAR文件: mail.jar和activation.jar,它包含了JavaMail API中所有的接口和类。
下载地址为:https://www.oracle.com/java/technologies/java-archive-eepla-downloads.html#javamail-1.4.5-oth-JPR;
3. 安装和配置Mail服务器
为了运行本课程介绍的程序,需要准备可以访问的邮件服务器,这里我们采用MerakMailServer服务器;它是一个商业服务器,支持E-mail协议;从以下网站可下载MerakMailServer的免费试用版本:http://www.javathinker.org/download/software/mailserver.zip
MerakMail服务器安装软件会自动在Window操作系统中加入邮件发送和接收服务,发送邮件采用SMTP协议,接收邮件支持POP3和IMAP协议。每次启动操作系统时,会自动运行这两项服务。
安装过程中会出现配置Domain窗口:可按照如图配置
MerakMail服务器的管理窗口:可以看到我们刚才创建的admin账户
4. 创建Java Mail应用客户程序
MailTest.java:由java语言编写的简单的邮件客户程序
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
import java.util.*;
public class MailTest{
public static void main(String[] args)throws Exception { //通过Java Mail API来访问邮件服务器的基本过程
Properties props = new Properties();
props.put("mail.smtp.host", "localhost");
Session session = Session.getDefaultInstance(props);
String toAddr="admin@mydomain.com";
String fromAddr="admin@mydomain.com";
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");
Transport.send(msg);
}
}
在访问邮件服务器之前,首先要进行初始化操作
4.1 创建Java Mail应用程序的初始化步骤
(1)设置Java Mail属性:
//set properties
Properties 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”); //支持SMTP协议的具体类
props.put(“mail.imap.class”, “com.sun.mail.imap.IMAPStore”); //支持TMAP协议的类
props.put(“mail.smtp.host”, hostname); //支持SMTP协议的IP地址
(2)调用javax.mail.Session类的静态方法Session.getDefaultInstance获取Session实例,该方法将根据已经配置的Java Mail属性来创建Session实例:
Session mailsession = Session.getDefaultInstance(props, null);
(3)调用Session的getStore方法来取得Store对象,在本例中,将获取采用imap协议的Store对象:
Store store = mailsession.getStore(“imap”);
根据配置的mail.imap.class属性,可以确定这里getStore方法返回com.sun.mail.imap.IMAPStore类型的Store对象。
(4)调用Store的connect方法连接到接收邮件服务器上的特定邮件账号。调用connect方法时你应该指定接收邮件服务器名称或IP地址、(登录到邮件服务器上的)邮件账户名和口令。
store.connect(hostname,username, password);
4.2 发送邮件
创建并发送邮件:MailTest.java
// create a 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");
//send a message
Transport.send(msg); //mail.smtp.host属性决定邮件发送服务器
4.3 访问邮件服务器上的特定邮件账号
打开inbox邮件夹参看邮件信息:MailTest.java
//check inbox
Folder folder=store.getFolder("inbox");
folder.open(Folder.READ_WRITE);
System.out.println("You have "+folder.getMessageCount()+" messages in inbox."); //getMessageCount()邮件总数
System.out.println("You have "+folder.getUnreadMessageCount()+" unread messages in inbox."); //getUnreadMessageCount()未读邮件总数
从邮件夹中读取信件:(邮件夹中的邮件都是排序的,第一份序号为1)
//read first Message in inbox
Message msg=folder.getMessage(1); //msg:返回的第一份邮件
System.out.println("------the first message in inbox-------");
System.out.println("From:"+msg.getFrom()[0]);
System.out.println("Subject:"+msg.getSubject()); //邮件标题
System.out.println("Text:"+msg.getContent());
4.4 运行MailTest类
在helloapp目录下运行:ant runmail
build.xml文件中定义了runmail target:
<target name="runmail" description="Run a sample" >
<java classname="MailTest" fork="true">
<classpath refid="compile.classpath"/>
</java>
</target>
5. Java Mail Web应用的分层结构
应用的三层结构:
6. Java Mail Web应用的功能
登录IMAP服务器上的邮件账号
管理邮件夹(创建,删除,重命名)
查看邮件夹中的邮件信息(邮件数量,未读邮件数量)
查看邮件内容
创建和发送邮件
退出邮件系统
6.1 Javamail应用的文件清单
源代码:https://download.csdn.net/download/qq_48666555/85406313(免费)
6.1.2 用于保存邮件账号信息的MailUserData JavaBean
当客户登录到邮件服务器后,他的邮件账号信息保存在MailUserData对象中,MailUserData对象作为JavaBean存放在HTTP会话范围内。
在MailUserData中定义了如下属性,并为这些属性提供了相应的get和set方法:
URLName urlName; //客户连接邮件服务器上的邮件账号的URL
Session session; //客户当前使用的邮件会话
Store store; //客户当前使用的Store
Folder currFolder; //客户当前访问的Folder文件夹
Message currMsg; //客户当前访问的Message
MailUserData类的实用方法
下面来看一看MailUserData类的源代码
package mypack;
/**
* This class is used to store session data for each user's session. It
* is stored in the HttpSession.
*/
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;
public class MailUserData {
URLName urlName;
Session session;
Store store;
Folder currFolder;
Message currMsg;
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;
}
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);
}
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 { //删除特定文件夹f里的若份邮件 arrayOpt[]:邮件序号
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(); //出现DETELE标志的邮件,则永久删除该邮件
}
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();
}
}
6.1.3 登录IMAP服务器上的邮件账号(login.jsp)
在邮件服务器已经配置好的环境下,登录邮件账号,因为login.jsp表单的action是提交到connect.jsp处理,connect.jsp负责根据用户的登录信息连接邮件服务器上的账号,如果连接成功,该请求又转发到listallfolders.jsp
6.1.4 管理邮件夹(listallfolders.jsp)
listallfolders.jsp显示用户的邮件夹信息,并提供了一些功能:
6.1.5 查看邮件夹中的邮件信息(listonefolder.jsp)
listonefolder.jsp显示用户指定邮件夹的信息
6.1.6 查看邮件内容(showmessage.jsp)
6.1.7 创建和发送邮件(compose.jsp)
6.1.8 退出邮件系统(logout.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>
7. 总结
8. 练习题
-
问题:以下哪些属于接收邮件的协议?(多选)
选项:
(A)POP3
(B)SMTP
(C)HTTP
(D)IMAP4
答案:A,D -
问题:以下哪个协议允许管理远程邮件服务器上的邮件夹?(单选)
选项:
(A)POP3
(B)SMTP
(C)HTTP
(D)IMAP4
答案:D -
问题:以下哪些方法属于javax.mail.Store类的方法?(多选)
选项:
(A)connect(String host,String user,String password)
(B)getFolder(String name)
(C)send(Message msg)
(D)setText(String txt)
(E)close()
答案:A,B,E
(A)connect(String host,String user,String password) //建立与邮件服务器的连接
(B)getFolder(String name) //获得参数名的邮件
(C)send(Message msg) //Transport类的静态方法,负责放松邮件
(D)setText(String txt) //Message类的静态方法,负责设置纯文本内容
(E)close() //断开与邮件服务器的连接
- 问题:Folder类的expunge()方法有什么作用?(单选)
选项:
(A)永久删除邮件夹中所有设置了DELETE标记的邮件
(B)永久删除邮件夹中所有的邮件
(C)给邮件夹中所有邮件加上DELETE标记
(D)取消邮件夹中所有邮件的DELETE标记
答案:A