JavaMail在Java EE应用程序中,经常需要发送E-mail。Java EE框架为应用提供了JavaMail接口,通过JavaMail相关的接口可以读取邮件服务器的邮件,并且可以完成邮件的发送过程。 本章的主要内容包括: ? E-mail体系结构 ? JavaMail API ? 如何使用JavaMail API发送邮件 ? 如何使用JavaMail API接收邮件 29.1 E-mail体系结构29.1.1 什么是E-mailE-mail是用户间或应用程序间交换信息的Internet标准。每个用户都有自己的网上邮箱,发送方把信息发送到自己的网上信箱,并声明信息的接收方;该信箱所在的“邮局”会把信息发送到接收方的“邮局”,接收方从接收方的“邮局”中自己的信箱获取信息。这样就完成了信息从发送方到接收方的传递。所以,要发送或者接收邮件首先应该有自己的邮箱。 E-mail消息可以包含普通文本,也可以包含更为复杂的多媒体数据类型和图像声音等。这样,用户就可以交换各种各样的信息。 每个E-mail消息的头信息中都包含消息的发出者、发送的目的地和其他相关信息。 29.1.2 E-mail体系结构要完成消息的交互,需要几方面的支持:邮件发送客户端程序、邮件接收客户端程序、邮件发送服务器和邮件接收服务器,此外,还需要相关的通信协议。 邮件发送客户端程序和邮件接收客户端程序可以是相同的,例如经常使用的微软的Outlook既可以发送邮件,也可以接收邮件。 邮件发送服务器和邮件接收服务器也可以是相同的服务器。在与邮件服务器交互的过程中,主要完成两个动作,把邮件发送到邮件服务器,以及从邮件服务器读取邮件。所以,主要使用两类协议,分别进行邮件的发送和接收。 邮件从发送方到接收方的传递过程(参见图29.1)如下: (1)邮件发送方通过邮件发送客户端把邮件发送到发送方的邮件服务器,在发送的过程中需要用到SMTP协议。 (2)发送方的邮件服务器把邮件发送到接收方的邮件服务器,使用的协议也是SMTP。 (3)邮件接收方从接收方邮件服务器接收邮件,使用POP3协议或者IMAP协议。
图29.1 邮件从发送方到接收方的传递过程 29.1.3 E-mail相关的协议? 简单邮件传输协议(SMTP) 在邮件发送方把邮件发送到发送方邮件服务器的时候,需要用到简单邮件传输协议(Simple Mail Transport Protocol,简称SMTP),发送方邮件服务器把邮件发送到接收方邮件服务器的时候,也要用到SMTP。SMTP是应用程序与邮件服务器通信并发送邮件的Internet标准,SMTP通信基于TCP协议端口25之上。 ? 邮箱协议(POP3) 邮件接收方从接收方邮件服务器接收邮件的时候,需要使用检索协议,可以使用POP3或者IMAP。POP3是Post Office Protocol的简称,是用于接收方从邮件服务器上检索E-mail消息的协议,工作在TCP协议端口110之上。 ? Internet消息访问协议(IMAP) IMAP是Internet Message Access Protocol的简称,与POP3基本相同。完成的主要功能是从邮件服务器接收邮件,使用的端口是293。 29.1.4 什么是Java Mail在Java EE应用中,经常需要通过E-mail与用户进行交互,主要是与邮件服务器的交互,例如向邮件服务器发送邮件,或者从邮件服务器接收邮件。如果是用户之间直接进行交互可以使用邮件客户端程序,例如微软的Outlook。如果想在应用程序中发送邮件,就不能直接使用通用的客户端了,需要编写自己专门的邮件发送和接收代码,并且需要与邮件服务器进行交互。可以通过Socket编程,使用相关的协议完成,但是这个过程非常复杂。而Java Mail提供了比较便利的解决方案。 JavaMail是Java EE中的标准API,是对邮件服务器访问过程的封装。使用JavaMail API则不需要编写与邮件服务器交互的详细过程,只需要调用相关接口即可。在接口中封装了与邮件服务器交互的详细过程。 本章的主要内容是介绍如何通过JavaMail API完成邮件的发送和接收。 29.2 JavaMail APIJavaMail API主要包括四个部分:Session,Message,Transport和InternetAddress。下面分别进行介绍。 29.2.1 SessionSession定义了全局的和每个用户的与邮件相关的属性,这些属性详细说明了客户机和服务器如何交流信息。Session中定义的属性如下: ? mail.store.protocol:确定检索邮件所使用的协议。可以是IMAP(接收),也可以是POP3。 ? mail.transport.protocol:确定发送邮件所使用的协议,可以是SMTP(发送)。 ? mail.host:确定邮件服务器的主机名。 ? mail.user:确定检索邮件或者发送邮件的用户名。 ? mail.protocol.host:确定具体的发送邮件服务器或者接收邮件服务器。有时候发送邮件服务器和接收邮件服务器使用的主机不同,这时候需要详细指定各个协议使用的主机,如果不指定,则使用mail.host所确定的主机。 ? mail.protocol.user:为登录特定邮件服务器所使用的用户名,如果没有指定,使用mail.user所指定的用户。 ? mail.from:为邮件指定默认的回复地址,如果没有指定,使用mail.user所指定的用户。 29.2.2 MessageMessage表示单个邮件消息,其属性包括消息类型、地址信息和所定义的目录结构。但是Message类是一个抽象类,必须实现它的一个子类。通常使用MimeMessage,它是Message类的一个派生类。 Message类的主要方法有两部分,第一部分主要在发送邮件的时候使用,用于设置邮件的相关信息,包括邮件的发送者、接收者、主题和发送时间等。这些方法如下: ? setFrom(),用于设置邮件的发送者,值从“mail.user”属性中获取,如果这个值是默认的,则使用系统属性“user.name”。 ? setFrom(Address address),与上一个方法的作用相同,值是通过参数确定的,是Address的对象,通常使用其实现者InternetAddress的对象作为参数。 ? addFrom(Address[] addresses),在已有的邮件发送者中添加其他的邮件发送者,参数是要添加的邮件发送者的地址。 ? setSubject(String subject),用于设置邮件的标题。 ? setContent(String contentType),用于设置邮件的内容类型。 ? setSentDate(java.util.Date date),用于设置邮件发送的时间。 ? setRecipient(Message.RecipientType type, Address address),用于设置邮件的接收者。有两个参数,第一个参数是接收者的类型,第二个参数是接收者。接收者类型可以是Message.RecipientType.TO,Message.RecipientType.CC和Message.RecipientType.BCC,TO表示主要接收人,CC表示抄送人,BCC表示秘密抄送人。接收者与发送者一样,通常使用InternetAddress的对象。 ? addRecipient(Message.RecipientType type, Address address),用于添加邮件的接收者,其参数与setRecipient方法的基本相同。 ? setRecipients(Message.RecipientType type, Address[] addresses),作用和setRecipient基本相同,区别在于该方法可以同时设置多个邮件的接收者。 ? addRecipients(Message.RecipientType type, Address[] addresses),用于添加邮件接收者,可以同时添加多个接收者。 ? setReplyTo(Address[] addresses),设置邮件的回复地址,参数用于确定要回复的地址。 ? setText(String text),用于设置邮件的文本,同时还将邮件的内容类型设置为text/plain。如果邮件的内容类型不是文本的,则需要通过setContent方法来设置内容类型。 第二部分用于获取邮件的相关信息,在接收邮件的时候使用: ? Flags getFlags(),用于获取与邮件相关的标记属性。 ? Folder getFolder(),用于获取该邮件所在的文件夹。 ? Address getFrom(),用于获取邮件的发送者。 ? int getMessageNumber(),用于获取邮件的编号,该编号是邮件系统设置的。 ? Address[] getAllRecipients(),获取邮件的所有接收者。 ? java.util.Date getReceivedDate(),用于获取邮件的接收时间。 ? Address[] getRecipients(Message.RecipientType type),用于获取指定接收类型的接收者,参数用于确定接收者的类型。 ? Address[] getReplyTo(),用于获取邮件的回复者,也就是邮件要给哪些人回复。 ? java.util.Date getSentDate(),用于获取邮件的发送时间。 ? java.lang.String getSubject(),用于获取邮件的主题。 29.2.3 TransportTransport是一个抽象类,用于邮件的发送,主要的方法有: ? public static void send(Message msg) throws MessagingException,用于发送邮件,参数就是要发送的邮件本身,该方法是一个静态方法,不需要实例化对象,可以直接使用。 ? public static void send(Message msg, Address[] addresses) throws MessagingException,也是用于发送邮件,有两个参数,第一个参数是要发送的邮件本身,第二个参数是邮件发送的目的地。该方法会忽略在邮件中设置的接收者。 29.2.4 InternetAddressInternetAddress把用户的E-mail地址映射为Internet地址。得到的邮件发送者和接收者通常都是字符串,但是在Message中确定邮件的接收者和发送者,以及在发送邮件时候使用的都是Address的对象。InternetAddress是Address的派生类,可以把字符串转换成InternetAddress类的对象。 构造函数如下: InternetAddress( ),无参数的默认构造函数。 InternetAddress(java.lang.String address),把一个字符串构造成一个InternetAddress,用得比较多。 InternetAddress(java.lang.String address, java.lang.String personal),使用字符串和个人姓名构造一个InternetAddress。 InternetAddress(java.lang.String address, java.lang.String personal, java.lang.String charset),使用字符串和个人姓名构造一个InternetAddress对象,名字的编码方式是第三个参数确定的。 要设置该对象所表示的Internet地址,可以通过下面的方法: void setAddress(java.lang.String address),参数确定了地址。 如果想把Internet地址转换成字符串,则使用下面的方法: java.lang.String toString() 29.3 WebLogic中邮件会话的配置在介绍邮件发送和邮件接收程序之前,需要在WebLogic中配置邮件会话的相关信息。需要配置所使用的发送邮件服务器和接收邮件服务器。在WebLogic中的配置过程如下: (1)进入到WebLogic Server的控制台; (2)在控制台的左下方选择【Domain Structure】→【Service】→【Mail Sessions】; (3)在控制台的左上方点击【Lock & Edit】; (3)在界面的右边点击【New】; (4)在接下来的过程中需要分别输入下面的信息: ? 会话的名字(Name):MailSession。 ? 会话的JNDI名字(JNDIName):MailSession;会话的JNDI名字可以和会话的名字相同。 ? 会话相关的属性: mail.pop3.host = 218.25.154.4 (应该写你所使用的邮件服务器的IP地址) mail.transport.protocol = smtp (发送) mail.user = lixucheng mail.smtp.host = 218.25.154.6 mail.store.protocol = pop3(接收) 其中mail.pop3.host是所使用的接收邮件服务器,mail.transport.protocol是发送邮件所使用的协议,mail.user是发送邮件和接收邮件时候的用户名,mail.smtp.host是所使用的发送邮件服务器,mail.store.protocol是检索邮件所使用的协议。 输入完这些信息之后,点击【Save】,创建该邮件会话。 (5)把邮件会话部署到相应的服务器上:选择【Targets】页面,从服务器列表中选择相应的服务器,然后点击【Save】,就完成邮件会话的配置了。要让之前的配置起作用,需要点击左上角的【Activate Changes】(参见图29.2和图29.3)。 29.4 邮件发送示例程序发送邮件的基本过程如下: (1)得到会话对象 (2)构造邮件对象 (3)发送邮件
图29.2 邮件会话的配置
图29.3 把会话部署到相应的服务器上 29.4.1 得到会话对象这里使用前面在WebLogic中配置好的邮件会话,所以首先要获取这个会话。下面的代码完成获取邮件会话的功能: //获取上下文环境 Context ctx = new InitialContext(); //从JNDI中查找会话MailSession Session mailSession = (Session) ctx.lookup("MailSession"); 其中,MailSession是我们在前面配置的邮件会话中的JNDI名字。 如果不使用配置好的邮件会话,可以通过创建Properties对象设置相关会话属性,然后,通过Session.getInstance(propoties, null)创建会话对象。 29.4.2 构造邮件对象发送一封邮件通常需要确定邮件发送者、邮件接收者、邮件的主题和邮件的内容,有时候还需要发送附件。这里先不考虑附件。其他的条件通过JSP界面接收,下面是构造邮件的代码: //获取相关参数 String to = request.getParameter("to"); String subject = request.getParameter("subject"); String from = request.getParameter("from"); String message = request.getParameter("message"); to = new String(to.getBytes("8859_1")); subject = new String(subject.getBytes("8859_1")); from = new String(from.getBytes("8859_1")); //创建消息对象 Message msg = new MimeMessage(mailSession);
//把邮件地址映射到Internet地址上 InternetAddress dest = new InternetAddress(to);
//设置消息内容 msg.setFrom(new InternetAddress(from)); msg.setSubject(subject); msg.setRecipient(Message.RecipientType.TO, dest); msg.setContent(message, "text/plain"); 首先,通过request对象的getParameter方法获取邮件接收者、邮件发送者、邮件主题和邮件内容。然后,创建Message的对象。最后,通过Message的setXXX方法设置邮件的相关信息。 29.4.3 发送邮件通过Transport的send(Message msg)方法发送构建好的消息。 Transport.send(msg); 29.4.4 完整的代码该实例的代码分为两部分,第一部分是发送邮件的界面,第二部分是发送邮件的处理代码。完整的代码如下: <%@ page import = "java.util.*, javax.mail.*, javax.mail.internet.*, javax.naming.*" %> <%@ page contentType = "text/html;charset = gb2312"%> <html> <head> <title>Mail Sender JSP</title> </head> <body>
<%
if (request.getMethod().equals("POST")) {
try {
//获取相关参数 String to = request.getParameter("to"); String subject = request.getParameter("subject"); String from = request.getParameter("from"); String message = request.getParameter("message"); to = new String(to.getBytes("8859_1")); subject = new String(subject.getBytes("8859_1")); from = new String(from.getBytes("8859_1")); message = new String(message.getBytes("8859_1")); //获取上下文环境 Context ctx = new InitialContext();
//从JNDI中查找会话MailSession Session mailSession = (Session) ctx.lookup("MailSession");
//创建消息对象 Message msg = new MimeMessage(mailSession);
//把邮件地址映射到Internet地址上 InternetAddress dest = new InternetAddress(to);
//设置消息内容 msg.setFrom(new InternetAddress(from)); msg.setSubject(subject); msg.setRecipient(Message.RecipientType.TO, dest); msg.setContent(message, "text/plain");
//通过Transport类发送消息 Transport.send(msg);
out.println("<h2>到 " + to + "的邮件发送成功!<h2>");
} catch (Exception e) {
out.println(e);
} } else {
%>
<h1>发送邮件!</h1> <form method = "post" action = "mailsender.jsp">
To :<input type = "text" name = "to" size = 16><p>
From :<input type = "text" name = "from" size = 16><p>
Subject :<input type = "text" name = "subject" size = 16><p>
Message :<input type = "text" name = "message" size = 16> <p> <input type = "submit" value = "Submit" name = "Command">
<% } %> </body> </html> 29.4.5 程序的运行结果直接访问mailsender.jsp文件时候的结果,这时候使用的请求方式是get,所以显示邮件发送的界面,参见图29.4。
图29.4 邮件发送的界面 图29.5显示了邮件发送成功的界面。填写完邮件的信息之后,提交给服务器,这时候的请求方式是Post。首先获取用户输入的与邮件相关的信息,然后把邮件发送出去。
图29.5 邮件发送成功的界面 29.4.6 发送HTML格式的邮件HTML格式的邮件发送过程与前面文本类型邮件的发送过程基本相同,不同的是需要读取邮件的HTML文件,同时,需要设置邮件内容的格式。 先获取邮件的内容: String content = ""; String file = "source.htm"; //要发送的html文件 String line = ""; FileReader f = new FileReader(file); BufferedReader b = new BufferedReader(f); While((line = b.readline()) != null) content += line; 然后设置邮件的内容,把邮件的内容添加到邮件对象中: message.setContent(content, "text/html"); 29.4.7 发送带附件的邮件我们经常需要发送带附件的邮件。带附件的邮件发送过程与前面介绍的普通邮件发送过程基本相同,不同的是邮件本身的构造比较麻烦。下面我们介绍如何发送带附件的邮件,有些代码和前面是相同的,所以这里只介绍与前面不同的代码。 创建一个带附件的邮件的过程如下: ? 创建BodyPart对象,该对象表示邮件的主体或者邮件的附件。 ? 把所有的BodyPart对象添加到MimeMultipart对象中。 ? 把MimeMultipart对象添加到MimeMessage对象中。 (1)BodyPart对象的创建 BodyPart是抽象类,具体使用的是MimeBodyPart类的对象,而MimeBodyPart是BodyPart类的派生类。组成邮件的各部分都是MimeBodyPart的对象。 首先,创建邮件的主体部分: BodyPart messagebody = new MimeBodyPart(); //创建BodyPart类的对象 messagebody.setText("带附件的邮件,注意查看附件!"); //设置邮件的内容 这样就创建了邮件的第一部分,也就是邮件的主体部分,下面创建邮件的附件部分。其中filename表示附件的名字。 BodyPart attachment = new MimeBodyPart(); //创建BodyPart对象,用于表示邮件的附件 String filename = "filename"; //要发送的邮件附件的名字 DataSource ds = new FileDataSource(filename); //创建数据源,数据源指向附件文件 attachment.setDataHandler(new DataHandler(ds)); //把附件BodyPart对象指向ds attachment.setFileName(filename); //设置附件的文件名 这样把邮件的主体部分和邮件的附件部分全部创建完了。 (2)把Body对象添加到MimeMultipart对象中 邮件的各个组成部分不能独立添加到MimeMessage对象中,可以添加到其中的只能是Multipart的对象,而组成邮件的各个部分可以分别添加到Multipart对象中。下列代码完成的功能是把前面创建好的邮件的两部分添加到Multipart对象中。 Multipart multipart = new MimeMultipart(); //创建MimeMultipart对象 multipart.addBodyPart(messagebody); //把messagebody添加到multipart对象中 multipart.addBodyPart(attachment); //把附件添加到multipart中 (3)把MimeMultipart对象添加到MimeMessage中 把MimeMultipart对象添加到MimeMessage对象中的方法非常简单,与前面介绍的基本相同。 message.setContent(multipart); 把MimeMultipart对象添加到message中之后,邮件的构造就完成了。邮件的发送过程与前面介绍的简单邮件的发送过程相同。 29.5 邮件接收示例程序邮件接收的基本过程如下: ? 获得邮件会话; ? 创建Store对象; ? 连接到邮件服务器; ? 得到默认的文件夹; ? 得到所要操作的文件夹; ? 打开文件夹,可以获取与文件夹相关的信息; ? 获取文件夹中的所有邮件; ? 获取邮件相关的信息。 29.5.1 获得邮件会话与发送邮件相同,接收邮件也需要获取相关的邮件会话。可以使用在WebLogic中配置好的邮件会话,也可以通过Properties对象保存邮件会话相关的信息,然后通过该Properties对象创建邮件会话。下面的代码使用的是WebLogic中配置好的邮件会话。在运行程序之前,需要保证配置好邮件会话。 Context ctx = new InitialContext(); //创建上下文环 Session mailsession = (Session)ctx.lookup("MailSession"); //得到邮件会话 29.5.2 创建Store对象要检索邮件服务器上的邮件,需要连接到邮件服务器,与邮件服务器的连接可以通过Store对象来完成。Store对象是通过Session的getStore方法创建的。 Store store = mailsession.getStore(); //创建存储对象 29.5.3 连接到邮件服务器连接到邮件服务器,需要知道邮件服务器的地址、连接邮件服务器的用户名以及该用户的口令。连接过程是通过Store对象的connect方法完成的。该方法需要三个参数,第一个参数为邮件服务器的地址、域名或者IP地址,第二个参数是用户名,第三个参数是口令。 store.connect("218.25.154.4","lixucheng","123456"); //连接到邮件服务器 29.5.4 得到默认的文件夹连接到邮件服务器之后,可以得到一个默认的文件夹,通过它可以获得其他的文件夹。默认文件夹是通过Store对象的getDefaultFolder方法得到的。文件夹是邮件的存储地方。 Folder defaultFolder = store.getDefaultFolder(); //得到默认的文件夹 29.5.5 得到所要操作的文件夹要访问邮件,需要得到邮件所在的文件夹,可以通过默认文件夹得到该文件夹,defaultFolder的list方法可以得到所有的文件夹。 Folder[] allfolder = defaultFolder.list(); 也可以通过Store对象的getFolder方法得到想要的文件夹。该方法需要一个参数,用于指定要打开的文件夹的名字。如果想得到INBOX文件夹,可以通过下面的方法。 Folder folder = store.getFolder("INBOX"); 29.5.6 打开文件夹,可以获取与文件夹相关的信息可以通过Folder对象的open方法打开文件夹。该方法需要一个参数,指定了文件夹的打开方式。文件夹的打开方式有两种:READ_ONLY和READ_WRITE。如果只是检索邮件信息而不改变其状态或内容,则应该使用READ_ONLY打开方式。下面的代码是分别打开邮箱中的文件夹,并获得文件夹的相关信息。 for(int i = 0;i<allfolder.length;i++) { allfolder[i].open(Folder.READ_ONLY); out.println(allfolder[i].getName()); out.println("/t"+allfolder[i].getMessageCount()); out.println("/t"+allfolder[i].getNewMessageCount()); out.println("/t"+allfolder[i].getUnreadMessageCount()); out.println("<br>"); allfolder[i].close(false); } 其中,getName( )方法用于获取文件夹的名字,getMessageCount得到文件夹中邮件的数量,getNewMessageCount得到文件夹中新邮件的数量,getUnreadMessageCount得到文件夹中未读邮件的数量。读取文件夹的信息之后,需要关闭文件夹。 29.5.7 获取文件夹中的所有邮件要获取文件夹中的邮件,首先要打开文件夹。获取邮件是通过文件夹的getMessages方法来完成的。 defaultFolder = allfolder[0]; defaultFolder.open(Folder.READ_ONLY); //打开该文件夹 Message[] messages = defaultFolder.getMessages(); //得到邮件信息 29.5.8 获取邮件相关的信息得到邮件对象Message之后,就可以获取邮件相关的信息以及邮件的内容了。下面的代码输出了邮件的相关信息: Message message = messages[j]; out.println("<tr><td>"+message.getFrom()[0].toString()+"</td>"); out.println("<td>"+message.getSubject()+"</td>"); out.println("<td>"+message.getSentDate().toLocaleString()+"</td>"); out.println("<td>"+message.getSize()+"</td></tr>"); out.println("<td>"+(String)message.getContent()+"</td></tr>"); 其中,getFrom( )得到邮件的发送者,getSubject得到邮件的主题,getSentDate得到邮件的发送时间,getSize得到邮件的大小,getContent得到邮件的内容。 29.5.9 邮件的状态邮件可以处于不同的状态,使用系统标记来标识邮件的状态,可以用Flags.Flag类中的静态成员变量来标记。共有七种:ANSWERED,DELETED,DRAFT,FLAGGED,RECENT,SEEN和USER。 ANSWERED表示该邮件已经被回复,当客户端对该信息回复后可以把该邮件标记为ANSWERED。 DELETED表示该邮件已经被删除,对某个文件夹的删除操作将删除该文件夹中所有标记为DELETED的邮件。 DRAFT表示该邮件是草稿,这样就不会被发送。 FLAGGED标记没有定义明确的语义,客户端可以根据自己的需要来使用该标记。 RECENT表示该邮件是最近一段时间的,是上次文件夹打开之后到达的邮件。 SEEN表示该邮件被查看过,当该邮件的内容以某种方式被用户得到后该邮件标记为SEEN,一般情况下调用Message的getInputStream和getContent方法会使得该标识得到设置。 USER是一个特殊的标志,表示文件夹支持用户自定义的标志。 getFlags( )可以得到邮件的标记,该方法返回的是Flags对象,要获得所有的标记,通过Flags对象的getSystemFlags方法获得具体标记的集合,而具体标记是Flags.Flag的对象。 isSet方法用于判断该邮件是否被设置了某个标记,而参数是某个给定的标志。如果邮件被设置了该标记,返回值是true;如果邮件没有被设置该标记,返回值为false。该方法通常用于判断邮件的状态。 setFlag用于设置邮件的状态,共有两个参数。第一个参数是所设置的标识的类型,第二个参数是值,取值为true或者false。 setFlags方法与setFlag方法的作用大致相同,不同之处在于可以同时设置多个标识。 29.5.10 接收邮件的完整代码接收邮件的完整代码如下: <%@ page import = "java.util.*, javax.mail.*, javax.mail.internet.*, javax.naming.*" %> <%@ page contentType = "text/html;charset = gb2312"%> <!doctype html public "-//w3c/dtd HTML 4.0//en"> <html> <head> <title>Mail Sender JSP</title> </head> <body> <% try { Context ctx = new InitialContext(); //创建上下文环 Session mailsession = (Session)ctx.lookup("MailSession"); //得到邮件会话 Store store = mailsession.getStore(); //创建存储对象 store.connect("218.25.154.4","lixucheng","123456"); //连接到邮件服务器 Folder defaultFolder = store.getDefaultFolder(); //得到默认的文件夹 Folder[] allfolder = defaultFolder.list(); for(int i = 0;i<allfolder.length;i++) { allfolder[i].open(Folder.READ_ONLY); out.println(allfolder[i].getName()); out.println("/t"+allfolder[i].getMessageCount()); out.println("/t"+allfolder[i].getNewMessageCount()); out.println("/t"+allfolder[i].getUnreadMessageCount()); out.println("<br>"); allfolder[i].close(false); } defaultFolder = allfolder[0]; defaultFolder.open(Folder.READ_ONLY); //打开默认文件夹 Message[] messages = defaultFolder.getMessages(); //得到邮件信息 out.println("邮件的数量:"+messages.length); if(messages.length>0) { out.println("<table>"); out.println("<tr><td>发送者</td><td>主题</td><td>接收时间</td><td>大小</td><td>内容</td></hr>"); for(int j = 0;j<messages.length;j++) { Message message = messages[j]; out.println("<tr><td>"+message.getFrom()[0].toString()+"</td>"); out.println("<td>"+message.getSubject()+"</td>"); out.println("<td>"+message.getSentDate().toLocaleString()+"</td>"); out.println("<td>"+message.getSize()+"</td></tr>"); out.println("<td>"+(String)message.getContent()+"</td></tr>"); } out.println("</table>"); } defaultFolder.close(false); store.close(); }catch(Exception e) { out.println(e.toString()); e.printStackTrace(); }
%> </body> </html>
package org.davidfang.mail; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Properties; import javax.mail.BodyPart; import javax.mail.Flags; import javax.mail.Folder; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.Store; import javax.mail.URLName; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; public class ReciveMail {
private MimeMessage msg = null; private String saveAttchPath = ""; private StringBuffer bodytext = new StringBuffer(); private String dateformate = "yy-MM-dd HH:mm";
public ReciveMail(MimeMessage msg){ this.msg = msg; } public void setMsg(MimeMessage msg) { this.msg = msg; }
/** * 获取发送邮件者信息 * @return * @throws MessagingException */ public String getFrom() throws MessagingException{ InternetAddress[] address = (InternetAddress[]) msg.getFrom(); String from = address[0].getAddress(); if(from == null){ from = ""; } String personal = address[0].getPersonal(); if(personal == null){ personal = ""; } String fromaddr = personal +"<"+from+">"; return fromaddr; }
/** * 获取邮件收件人,抄送,密送的地址和信息。根据所传递的参数不同 "to"-->收件人,"cc"-->抄送人地址,"bcc"-->密送地址 * @param type * @return * @throws MessagingException * @throws UnsupportedEncodingException */ public String getMailAddress(String type) throws MessagingException, UnsupportedEncodingException{ String mailaddr = ""; String addrType = type.toUpperCase(); InternetAddress[] address = null;
if(addrType.equals("TO")||addrType.equals("CC")||addrType.equals("BCC")){ if(addrType.equals("TO")){ address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.TO); } if(addrType.equals("CC")){ address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.CC); } if(addrType.equals("BCC")){ address = (InternetAddress[]) msg.getRecipients(Message.RecipientType.BCC); }
if(address != null){ for(int i=0;i<address.length;i++){ String mail = address[i].getAddress(); if(mail == null){ mail = ""; }else{ mail = MimeUtility.decodeText(mail); } String personal = address[i].getPersonal(); if(personal == null){ personal = ""; }else{ personal = MimeUtility.decodeText(personal); } String compositeto = personal +"<"+mail+">"; mailaddr += ","+compositeto; } mailaddr = mailaddr.substring(1); } }else{ throw new RuntimeException("Error email Type!"); } return mailaddr; }
/** * 获取邮件主题 * @return * @throws UnsupportedEncodingException * @throws MessagingException */ public String getSubject() throws UnsupportedEncodingException, MessagingException{ String subject = ""; subject = MimeUtility.decodeText(msg.getSubject()); if(subject == null){ subject = ""; } return subject; }
/** * 获取邮件发送日期 * @return * @throws MessagingException */ public String getSendDate() throws MessagingException{ Date sendDate = msg.getSentDate(); SimpleDateFormat smd = new SimpleDateFormat(dateformate); return smd.format(sendDate); }
/** * 获取邮件正文内容 * @return */ public String getBodyText(){
return bodytext.toString(); }
/** * 解析邮件,将得到的邮件内容保存到一个stringBuffer对象中,解析邮件 主要根据MimeType的不同执行不同的操作,一步一步的解析 * @param part * @throws MessagingException * @throws IOException */ public void getMailContent(Part part) throws MessagingException, IOException{
String contentType = part.getContentType(); int nameindex = contentType.indexOf("name"); boolean conname = false; if(nameindex != -1){ conname = true; } System.out.println("CONTENTTYPE:"+contentType); if(part.isMimeType("text/plain")&&!conname){ bodytext.append((String)part.getContent()); }else if(part.isMimeType("text/html")&&!conname){ bodytext.append((String)part.getContent()); }else if(part.isMimeType("multipart/*")){ Multipart multipart = (Multipart) part.getContent(); int count = multipart.getCount(); for(int i=0;i<count;i++){ getMailContent(multipart.getBodyPart(i)); } }else if(part.isMimeType("message/rfc822")){ getMailContent((Part) part.getContent()); }
}
/** * 判断邮件是否需要回执,如需回执返回true,否则返回false * @return * @throws MessagingException */ public boolean getReplySign() throws MessagingException{ boolean replySign = false; String needreply[] = msg.getHeader("Disposition-Notification-TO"); if(needreply != null){ replySign = true; } return replySign; }
/** * 获取此邮件的message-id * @return * @throws MessagingException */ public String getMessageId() throws MessagingException{ return msg.getMessageID(); }
/** * 判断此邮件是否已读,如果未读则返回false,已读返回true * @return * @throws MessagingException */ public boolean isNew() throws MessagingException{ boolean isnew = false; Flags flags = ((Message)msg).getFlags(); Flags.Flag[] flag = flags.getSystemFlags(); System.out.println("flags's length:"+flag.length); for(int i=0;i<flag.length;i++){ if(flag[i]==Flags.Flag.SEEN){ isnew = true; System.out.println("seen message ......."); break; } }
return isnew; }
/** * 判断是是否包含附件 * @param part * @return * @throws MessagingException * @throws IOException */ public boolean isContainAttch(Part part) throws MessagingException, IOException{ boolean flag = false;
String contentType = part.getContentType(); if(part.isMimeType("multipart/*")){ Multipart multipart = (Multipart) part.getContent(); int count = multipart.getCount(); for(int i=0;i<count;i++){ BodyPart bodypart = multipart.getBodyPart(i); String dispostion = bodypart.getDisposition(); if((dispostion != null)&&(dispostion.equals(Part.ATTACHMENT)||dispostion.equals(Part.INLINE))){ flag = true; }else if(bodypart.isMimeType("multipart/*")){ flag = isContainAttch(bodypart); }else{ String conType = bodypart.getContentType(); if(conType.toLowerCase().indexOf("appliaction")!=-1){ flag = true; } if(conType.toLowerCase().indexOf("name")!=-1){ flag = true; } } } }else if(part.isMimeType("message/rfc822")){ flag = isContainAttch((Part) part.getContent()); }
return flag; }
/** * 保存附件 * @param part * @throws MessagingException * @throws IOException */ public void saveAttchMent(Part part) throws MessagingException, IOException{ String filename = ""; if(part.isMimeType("multipart/*")){ Multipart mp = (Multipart) part.getContent(); for(int i=0;i<mp.getCount();i++){ BodyPart mpart = mp.getBodyPart(i); String dispostion = mpart.getDisposition(); if((dispostion != null)&&(dispostion.equals(Part.ATTACHMENT)||dispostion.equals(Part.INLINE))){ filename = mpart.getFileName(); if(filename.toLowerCase().indexOf("gb2312")!=-1){ filename = MimeUtility.decodeText(filename); } saveFile(filename,mpart.getInputStream()); }else if(mpart.isMimeType("multipart/*")){ saveAttchMent(mpart); }else{ filename = mpart.getFileName(); if(filename != null&&(filename.toLowerCase().indexOf("gb2312")!=-1)){ filename = MimeUtility.decodeText(filename); } saveFile(filename,mpart.getInputStream()); } }
}else if(part.isMimeType("message/rfc822")){ saveAttchMent((Part) part.getContent()); } } /** * 获得保存附件的地址 * @return */ public String getSaveAttchPath() { return saveAttchPath; } /** * 设置保存附件地址 * @param saveAttchPath */ public void setSaveAttchPath(String saveAttchPath) { this.saveAttchPath = saveAttchPath; } /** * 设置日期格式 * @param dateformate */ public void setDateformate(String dateformate) { this.dateformate = dateformate; } /** * 保存文件内容 * @param filename * @param inputStream * @throws IOException */ private void saveFile(String filename, InputStream inputStream) throws IOException { String osname = System.getProperty("os.name"); String storedir = getSaveAttchPath(); String sepatror = ""; if(osname == null){ osname = ""; }
if(osname.toLowerCase().indexOf("win")!=-1){ sepatror = "//"; if(storedir==null||"".equals(storedir)){ storedir = "d://temp"; } }else{ sepatror = "/"; storedir = "/temp"; }
File storefile = new File(storedir+sepatror+filename); System.out.println("storefile's path:"+storefile.toString());
BufferedOutputStream bos = null; BufferedInputStream bis = null;
try { bos = new BufferedOutputStream(new FileOutputStream(storefile)); bis = new BufferedInputStream(inputStream); int c; while((c= bis.read())!=-1){ bos.write(c); bos.flush(); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ bos.close(); bis.close(); }
}
public void recive(Part part,int i) throws MessagingException, IOException{ System.out.println("------------------START-----------------------"); System.out.println("Message"+i+" subject:" + getSubject()); System.out.println("Message"+i+" from:" + getFrom()); System.out.println("Message"+i+" isNew:" + isNew()); boolean flag = isContainAttch(part); System.out.println("Message"+i+" isContainAttch:" +flag); System.out.println("Message"+i+" replySign:" + getReplySign()); getMailContent(part); System.out.println("Message"+i+" content:" + getBodyText()); setSaveAttchPath("c://temp//"+i); if(flag){ saveAttchMent(part); } System.out.println("------------------END-----------------------"); }
public static void main(String[] args) throws MessagingException, IOException { Properties props = new Properties(); props.setProperty("mail.smtp.host", "smtp.sina.com"); props.setProperty("mail.smtp.auth", "true"); Session session = Session.getDefaultInstance(props,null); URLName urlname = new URLName("pop3","pop.qq.com",110,null,"715881036","kingsoft");
Store store = session.getStore(urlname); store.connect(); Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); Message msgs[] = folder.getMessages(); int count = msgs.length; System.out.println("Message Count:"+count); ReciveMail rm = null; for(int i=0;i<count;i++){ rm = new ReciveMail((MimeMessage) msgs[i]); rm.recive(msgs[i],i);; }
}
public static void main(String[] args) throws MessagingException, IOException { // 连接pop3服务器的主机名、协议、用户名、密码 String pop3Server = "pop.mail.163.com"; String protocol = "pop3"; String user = "[email protected]"; String pwd = "password";
// 创建一个有具体连接信息的Properties对象 Properties props = new Properties(); props.setProperty("mail.store.protocol", protocol); props.setProperty("mail.pop3.host", pop3Server);
// 使用Properties对象获得Session对象 Session session = Session.getInstance(props); session.setDebug(true);
// 利用Session对象获得Store对象,并连接pop3服务器 Store store = session.getStore(); store.connect(pop3Server, user, pwd);
Folder folder = store.getFolder("INBOX"); folder.open(Folder.READ_ONLY); Message msgs[] = folder.getMessages(); int count = msgs.length; System.out.println("Message Count:" + count); ReciveMail rm = null;
for (int i = 0; i < count; i++) { rm = new ReciveMail((MimeMessage) msgs[i]); rm.recive(msgs[i], i); ; }
}
}
===============================================================================================================================
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Properties;
import javax.mail.Folder; import javax.mail.Message; import javax.mail.Session; import javax.mail.Store;
/** * 简单的邮件接收程序,打印出邮件的原始内容 * @author haolloyin */ public class SimpleStoreMails { public static void main(String[] args) throws Exception { // 连接pop3服务器的主机名、协议、用户名、密码 String pop3Server = "pop.mail.163.com"; String protocol = "pop3"; String user = "[email protected]"; String pwd = "password";
// 创建一个有具体连接信息的Properties对象 Properties props = new Properties(); props.setProperty("mail.store.protocol", protocol); props.setProperty("mail.pop3.host", pop3Server);
// 使用Properties对象获得Session对象 Session session = Session.getInstance(props); session.setDebug(true);
// 利用Session对象获得Store对象,并连接pop3服务器 Store store = session.getStore(); store.connect(pop3Server, user, pwd);
// 获得邮箱内的邮件夹Folder对象,以"只读"打开 Folder folder = store.getFolder("inbox"); folder.open(Folder.READ_ONLY);
// 获得邮件夹Folder内的所有邮件Message对象 Message [] messages = folder.getMessages();
int mailCounts = messages.length;
System.out.println(mailCounts);
for(int i = 0; i < mailCounts; i++) {
String subject = messages[i].getSubject(); String from = (messages[i].getFrom()[0]).toString();
System.out.println("第 " + (i+1) + "封邮件的主题:" + subject); System.out.println("第 " + (i+1) + "封邮件的发件人地址:" + from); System.out.println(messages[i].getContent().toString()); System.out.println("是否打开该邮件(yes/no)?:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String input = br.readLine(); if("yes".equalsIgnoreCase(input)) { // 直接输出到控制台中 messages[i].writeTo(System.out); } } System.out.println(mailCounts); folder.close(false); store.close(); } }
===================================================================================
1 电子邮件的需求
随着网络应用的不断推广,电子邮件越来越多的被大家使用。虽然我们往往将电子邮件与 Foxmail、Outlook这样的电子邮件客户端联系起来,但是往往在企业业务应用中我们也需要自己编程实现发送接收邮件的功能。
2 电子邮件的协议
邮件的收发,说白了就是邮件内容在特定的机器上的信息流转(发送和接收)。而信息的流转需要依靠网络协议的支持。
当前常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇,默认状态下,分别通过TCP端口25、110和143建立连接。
2.1 SMTP协议
SMTP的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循SMTP协议的发送邮件服务器。SMTP认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。
2.2 POP协议
POP的全称是 Post Office Protocol ,即邮局协议,用于电子邮件的接收,它使用TCP的110端口,现在常用的是第三版,所以简称为 POP3。邮局协议负责从邮件服务器中检索电子邮件。它要求邮件服务器完成下面几种任务之一:从邮件服务器中检索邮件并从服务器中删除这个邮件;从邮件服务器中检索邮件但不删除它;不检索邮件,只是询问是否有新邮件到达。
2.3 IMAP协议
IMAP的全称是 Internet Mail Access Protocol 即互联网信息访问协议,IMAP是斯坦福大学在1986年开发的研发的一种邮件获取协议,互联网信息访问协议(IMAP)是一种优于POP的新协议。和POP一样,IMAP也能下载邮件、从服务器中删除邮件或询问是否有新邮件,但IMAP克 服了POP的一些缺点。例如,它可以决定客户机请求邮件服务器提交所收到邮件的方式,请求邮件服务器只下载所选中的邮件而不是全部邮件。客户机可先阅读邮件信息的标题和发送者的名字再决定是否下载这个邮件。通过用户的客户机电子邮件程序,IMAP可让用户在服务器上创建并管理邮件文件夹或邮箱、删除邮件、查询某封信的一部分或全部内容,完成所有这些工作时都不需要把邮件从服务器下载到用户的个人计算机上。
2.4 各个协议的运用
简单邮件传输协议SMTP(Simple Mail Transfer Protocol)和邮局协议POP(Post Office Protocol)是负责用客户机/服务器模式发送和检索电子邮件的协议。用户计算机上运行的电子邮件客户机程序请求邮件服务器进行邮件传输,邮件服务器采用简单邮件传输协议标准。很多邮件传输工具,如outlook express、fox mail等,都遵守SMTP标准并用这个协议向邮件服务器发送邮件。SMTP协议规定了邮件信息的具体格式和邮件的管理方式。
SMTP的一个重要特点是它能够在传送中接力传送邮件,即邮件可以通过不同网络上的主机接力式传送。工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器。 SMTP是个请求/响应协议,它监听25号端口,用于接收用户关于邮件请求,并与远端邮件服务器建立SMTP连接。
POP3(Post Office Protocol 3)即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。POP3 服务是一种用来接收电子邮件的电子邮件服务。管理员可以使用 POP3 服务,在邮件服务器上存储和管理电子邮件帐户。在邮件服务器中安装了 POP3 服务后,用户即可使用支持 POP3 协议的电子邮件客户端(如 Outlook 或 Outlook Express)连接到邮件服务器,并将电子邮件接收到其本地计算机中。
3 邮件系统的架构
一个成熟的邮件系统,就相当于我们日常生活中的邮局,能够收到一些要待发出去的邮件,又能够投递出一些待发的邮件。
必须提供两种服务,就是SMTP的邮件发送服务,POP3的邮件接收服务。
一般来说一个成熟的邮件系统会是这样的架构: 1台smtp服务器, 1台pop3服务器, 1台应用+数据库服务器。
常用网站 接收服务器 发送服务器
@163.com邮箱 pop.163.com smtp.163.com
@126.com邮箱 pop3.126.com smtp.126.com
@sohu.com邮箱 pop3.sohu.com smtp.sohu.com
端口号一般是固定的:
SMTP :25
POP3 :110
4 邮件系统的安全处理
邮件系统的安全处理涉及到两个安全协议。
4.1 SSL
安全套接层 (Secure Sockets Layer ,SSL),是为网络通信提供安全及数据完整性的一种安全协议。
SSL协议提供的服务主要有:
1)认证用户和服务器,确保数据发送到正确的客户机和服务器;
2)加密数据以防止数据中途被窃取;
3)维护数据的完整性,确保数据在传输过程中不被改变。
4.2 TLS
传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。
TLS和SSL功能相同,用于通信之间提供保密性和数据完整性。
4.3 加密后的一般端口号
邮件经过安全加密处理后,端口号会发生相应的变化。
POP3服务器端口:995 支持SSL SMTP服务器端口: 端口:465 或者 587 支持SSL(TSL)
465端口是SSL/TLS通讯协议的 内容一开始就被保护起来了 是看不到原文的。 587端口是STARTTLS协议的 属于TLS通讯协议 只是他是在STARTTLS命令执行后才对之后的原文进行保护的。
1 JavaMail介绍 JavaMail,顾名思义,提供给开发者处理电子邮件相关的编程接口。它是Sun发布的用来处理email的API。它可以方便地执行一些常用的邮件传输。我们可以基于JavaMail开发出类似于Microsoft Outlook的应用程序。 虽然JavaMail是Sun的API之一,但它目前还没有被加在标准的java开发工具包中。 这就意味着你在使用前必须另外下载JavaMail文件。 JavaMail包中用于处理电子邮件的核心类是:Session,Message,Address,Authenticator,Transport,Store,Folder等。 2 没有安全加密设置的邮件读取 现在很多免费的邮箱没有进行安全加密设置 端口号 Smtp : 25 POP3 : 110, 通过JavaMail来实现邮件服务的收发,网上有很多很好的代码,实现起来也比较简单。 3 有SSL安全加密设置的邮件读取 一般的企业邮箱都会进行安全加密设置: POP3服务器地址端口:995 支持SSL SMTP服务器地址端口:465 或者 587 支持SSL(TSL) 465端口是SSL/TLS通讯协议的 内容一开始就被保护起来了 是看不到原文的。 587端口是STARTTLS协议的 属于TLS通讯协议 只是他是在STARTTLS命令执行后才对之后的原文进行保护的。 收邮件的代码如下: 主要的包如下: import java.io.UnsupportedEncodingException; import java.security.Security; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.logging.Level;
import javax.mail.Address; import javax.mail.BodyPart; import javax.mail.Folder; import javax.mail.Message; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Session; import javax.mail.Store; import javax.mail.URLName; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import com.sun.mail.pop3.POP3Folder; //接收邮件的主方法 public void mainReceive() throws Exception { // DummySSLSocketFactory 是一个网上现有的一个类,路径我放在com.mycom.receiveMail文件夹下 Security.setProperty( "ssl.SocketFactory.provider", "com.mycom.receiveMail.DummySSLSocketFactory"); final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
Properties props = System.getProperties(); props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY); props.setProperty("mail.pop3.socketFactory.fallback", "false"); props.setProperty("mail.pop3.port", "995"); props.setProperty("mail.pop3.socketFactory.port", "995"); //以下步骤跟一般的JavaMail操作相同--建立一个邮件对话 mail.myCom.com 服务 Session session = Session.getInstance(props); URLName urln = new URLName("pop3","mail.myCom.com",995,null, 'myUserNameLiwg', 'myUserPassLiwg'); Store store = session.getStore(urln);
Folder inbox = null; //创建数据库连接----------主要用于每次收不重复的邮件--还有就是可以储存读取的邮件标题,内容等信息---------- ConnectionPool conpool=new ConnectionPool(); Connection conn=conpool.getDbCon("oracle.properties"); try { store.connect(); inbox = store.getFolder("INBOX"); inbox.open(Folder.READ_ONLY); // 过滤已经下载过的邮件,而不用重复下载 利用邮件唯一的uid来实现,邮件服务器上的每一个邮件都有一个唯一标识 UID // 每一个邮件在邮件服务器上都有一个唯一的UID标识属性。 Message[] messages=getNewMessages(inbox,conn); if(messages!=null){ //解析收取到的邮件----把邮件相关的属性内容放在数据库中 insertMailInfo(messages,conn); } //提交数据库操作的事务 try{ conn.commit(); }catch(Exception e){ //回滚事务或给某些数据进行解锁 conn.rollback(); } //关闭数据库----------------- conn.close(); }catch (Exception ex){ ex.printStackTrace();
} finally { try { inbox.close(false); } catch (Exception e) {e.printStackTrace();} try { store.close(); } catch (Exception e) {e.printStackTrace();}
}
} // 通过邮件的UID 来实现不重复接收邮件-- 主要代码如下 public List getNewMessages(Folder folder,Connection conn) throws Exception{ POP3Folder inbox = (POP3Folder) folder; Message[] messages = inbox.getMessages(); Message[] messages2 = null; List noReadList=new ArrayList(); //读取数据库中是否存在这样的UID,如何存在,跳过,如果不存在,读取,并在UID表中插入一条记录,通过数据库的一个UID表来实现存储和判断。 //用查询的方式来实现判断 不用遍历数组的方式。 for (int i = 0; i < messages.length; i++) { MimeMessage mimeMessage = (MimeMessage) messages[i]; String uid = inbox.getUID(mimeMessage); if(!isExistUID(uid,conn)){ //添加-当符合条件的邮件才会被添加进list列表中, noReadList.add(messages[i]);
} }
return noReadList; } //读取有效的邮件信息,并把邮件信息插入到数据库中 //插入邮件的有效信息到表中 public void insertMailInfo(Message[] messages,Connection conn) throws Exception{ for(int m=0;m<messages.length;m++) { //邮件发送者的对象获取 String from = decodeText(messages[m].getFrom()[0].toString()); InternetAddress ia = new InternetAddress(from);
//邮件内容 StringBuffer bodytext = new StringBuffer(); //如果邮件有附件的时候 if (messages[m].getContent() instanceof Multipart) { Multipart mp = (Multipart) messages[m].getContent(); // 2012-03-14 只进行1次循环,我跟过代码,一次循环,就可以得到我需要的内容,两次循环,内容会重复取。 for (int t = 0; t < 1; t++) { BodyPart part = mp.getBodyPart(t); bodytext.append(getMailContent(part)); }
} //如果邮件就是直接的邮件--html格式-直接打印邮件内容 else if(messages[m].isMimeType("text/html")){ //html--转换成 text 文本 //其中html2text方法是我写的专门处理html内容转成text内容的方法。WebFormatter是我建立的一个类,包含一个html到text文本转换的方法。 String htmlToText=WebFormatter.html2text(messages[m].getContent().toString()); bodytext.append(htmlToText);
} //如果邮件就是直接的邮件--text--格式-直接打印邮件内容 else{ //直接的文本邮件 bodytext.append(messages[m].getContent()); } // 邮件标题 String mailTitle= messages[m].getSubject(); //发送人的邮件地址 String fromMail= ia.getAddress(); //邮件正文内容 String mailContext=tempBody; //得到邮件抄送人信息 String ccString=decodeText(getCCString(messages[m].getRecipients(Message.RecipientType.CC))); }
在网上有现成的DummySSLSocketFactory类如下: import com.sun.net.ssl.*; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; public class DummySSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory factory; public DummySSLSocketFactory() { try { SSLContext sslcontext = SSLContext.getInstance( "TLS"); sslcontext.init( null, // No KeyManager required new TrustManager[] { new DummyTrustManager()}, new java.security.SecureRandom()); factory = ( SSLSocketFactory) sslcontext.getSocketFactory(); } catch( Exception ex) { ex.printStackTrace(); } } public static SocketFactory getDefault() { return new DummySSLSocketFactory(); } public Socket createSocket() throws IOException { return factory.createSocket(); } public Socket createSocket( Socket socket, String s, int i, boolean flag) throws IOException { return factory.createSocket( socket, s, i, flag); } public Socket createSocket( InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException { return factory.createSocket( inaddr, i, inaddr1, j); } public Socket createSocket( InetAddress inaddr, int i) throws IOException { return factory.createSocket( inaddr, i); } public Socket createSocket( String s, int i, InetAddress inaddr, int j) throws IOException { return factory.createSocket( s, i, inaddr, j); } public Socket createSocket( String s, int i) throws IOException { return factory.createSocket( s, i); } public String[] getDefaultCipherSuites() { return factory.getSupportedCipherSuites(); } public String[] getSupportedCipherSuites() { return factory.getSupportedCipherSuites(); } } // DummySSLSocketFactory的依赖类 如下: import java.security.cert.X509Certificate;
import com.sun.net.ssl.X509TrustManager;
public class DummyTrustManager implements X509TrustManager{ public boolean isClientTrusted( X509Certificate[] cert) { return true; } public boolean isServerTrusted( X509Certificate[] cert) { return true; } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[ 0]; }
}
1 端口说明: 邮件经过安全加密处理后,端口号会发生相应的变化。 POP3服务器端口:995 支持SSL SMTP服务器端口: 端口:465 或者 587 支持SSL(TSL) 465端口是SSL/TLS通讯协议的 内容一开始就被保护起来了 是看不到原文的。 587端口是STARTTLS协议的 属于TLS通讯协议 只是他是在STARTTLS命令执行后才对之后的原文进行保护的。 2 发送邮件准备 1) 确认执行邮件发送程序的机器能够联通。 邮件发送服务器服务 如 我现在想连接 smtp.myCom.com。可以ping一下。 2) 生成一个SSL邮件认证文件。 sun网站上有相应的InstallCert.java文件。网上也有很多下载的地方。 InstallCert.java 的类文件全部内容为: **************************************************** /* * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Sun Microsystems nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.io.*; import java.net.URL; import java.security.*; import java.security.cert.*; import javax.net.ssl.*; public class InstallCert { public static void main(String[] args) throws Exception { String host; int port; char[] passphrase; if ((args.length == 1) || (args.length == 2)) { String[] c = args[0].split(":"); host = c[0]; port = (c.length == 1) ? 443 : Integer.parseInt(c[1]); String p = (args.length == 1) ? "changeit" : args[1]; passphrase = p.toCharArray(); } else { System.out.println("Usage: java InstallCert <host>[:port] [passphrase]"); return; } File file = new File("jssecacerts"); if (file.isFile() == false) { char SEP = File.separatorChar; File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security"); file = new File(dir, "jssecacerts"); if (file.isFile() == false) { file = new File(dir, "cacerts"); } } System.out.println("Loading KeyStore " + file + "..."); InputStream in = new FileInputStream(file); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(in, passphrase); in.close(); SSLContext context = SSLContext.getInstance("TLS"); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; SavingTrustManager tm = new SavingTrustManager(defaultTrustManager); context.init(null, new TrustManager[] {tm}, null); SSLSocketFactory factory = context.getSocketFactory(); System.out.println("Opening connection to " + host + ":" + port + "..."); SSLSocket socket = (SSLSocket)factory.createSocket(host, port); socket.setSoTimeout(10000); try { System.out.println("Starting SSL handshake..."); socket.startHandshake(); socket.close(); System.out.println(); System.out.println("No errors, certificate is already trusted"); } catch (SSLException e) { System.out.println(); e.printStackTrace(System.out); } X509Certificate[] chain = tm.chain; if (chain == null) { System.out.println("Could not obtain server certificate chain"); return; } BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.println(); System.out.println("Server sent " + chain.length + " certificate(s):"); System.out.println(); MessageDigest sha1 = MessageDigest.getInstance("SHA1"); MessageDigest md5 = MessageDigest.getInstance("MD5"); for (int i = 0; i < chain.length; i++) { X509Certificate cert = chain[i]; System.out.println (" " + (i + 1) + " Subject " + cert.getSubjectDN()); System.out.println(" Issuer " + cert.getIssuerDN()); sha1.update(cert.getEncoded()); System.out.println(" sha1 " + toHexString(sha1.digest())); md5.update(cert.getEncoded()); System.out.println(" md5 " + toHexString(md5.digest())); System.out.println(); } System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]"); String line = reader.readLine().trim(); int k; try { k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1; } catch (NumberFormatException e) { System.out.println("KeyStore not changed"); return; } X509Certificate cert = chain[k]; String alias = host + "-" + (k + 1); ks.setCertificateEntry(alias, cert); OutputStream out = new FileOutputStream("jssecacerts"); ks.store(out, passphrase); out.close(); System.out.println(); System.out.println(cert); System.out.println(); System.out.println ("Added certificate to keystore 'jssecacerts' using alias '" + alias + "'"); } private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray(); private static String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 3); for (int b : bytes) { b &= 0xff; sb.append(HEXDIGITS[b >> 4]); sb.append(HEXDIGITS[b & 15]); sb.append(' '); } return sb.toString(); } private static class SavingTrustManager implements X509TrustManager { private final X509TrustManager tm; private X509Certificate[] chain; SavingTrustManager(X509TrustManager tm) { this.tm = tm; } public X509Certificate[] getAcceptedIssuers() { throw new UnsupportedOperationException(); } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new UnsupportedOperationException(); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { this.chain = chain; tm.checkServerTrusted(chain, authType); } } }
****************************************************** 运行 javac InstallCert.java 生成 InstallCert.class文件。 运行 java InstallCert smtp.myCom.com:587。 得到jssecacerts文件后复制到一个文件夹目录下,如我放的路径为 D:\MailServer文件夹路径下。 3 发送邮件的主要代码如下: 主要的引用类为 import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.Security; import java.util.Date; import java.util.Properties; import java.util.logging.Level;
import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage;
protected void sentMail() throws SQLException { // 邮箱的用户名 String username = "weiguolee"; //邮件的密码 String password = "weiguolee123"; //安全认证文件地址 网上有现成的安全认证文件的原类,以及生成安全认证文件的具体方法。是一个jssecacerts文件 String address = "D:\MailServer\jssecacerts";
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory"; // Get a Properties object System.setProperty("javax.net.ssl.trustStore", address); Properties props = System.getProperties(); //smtp.myCom.com 为 公司邮件服务器的发送邮件服务 props.setProperty("mail.smtp.host", "smtp.myCom.com"); props.setProperty("mail.smtp.starttls.enable", "true"); props.setProperty("mail.smtp.socketFactory.fallback", "false"); props.setProperty("mail.smtp.port", "587"); props.setProperty("mail.smtp.socketFactory.port", "587"); props.put("mail.smtp.auth", "true"); Session session = Session.getDefaultInstance(props, new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); Message msg = new MimeMessage(session); try { // "公司邮件通知系统" 为 发送邮件时候,在别人邮箱中显示的发件人名称。 msg.setFrom(new InternetAddress(username + "@myCom.com","公司邮件通知系统")); //发送人为 [email protected] String sendMail="[email protected]"; msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(sendMail,false)); msg.setSubject("这是发送邮件的标题"); msg.setText("这是发送邮件的主要内容"); //邮件的发送日期 msg.setSentDate(new Date()); // 调用javaMail中的发送对象和方法进行邮件发送。 Transport.send(msg); } catch (AddressException e) { e.printStackTrace(); } catch (MessagingException e) { e.printStackTrace(); }catch (UnsupportedEncodingException e) { e.printStackTrace(); }
}
|