用Java做邮件服务器的简易客户端
武昌造船厂(430060) 晁扬扬
【摘 要】 Java是一种跨平台,适合于分布式计算环境的面向对象编程语言。具体来说,它具有如下特性:简单性、面向对象、分布式、解释型、可靠、安全、平台无关、 可移植、高性能、多线程、动态性等。Java始于网络,服务于网络,用Java做的网络程序,随处可见,这里我就不多做介绍了,下面给大家接收一种用Java实现的GUI界面的邮件收发系统。
本文是一篇入门级教程,主要讲了1)利用Eclipse这个优秀的Java IDE中创建应用程序界面;2)利用J2EE的API构建自己的邮件收发系统。要求您对JavaMail和JavaBeans和GUI的一些相关知识。
所用到的工具包括Eclipse(是一个JAVA IDE)、j2ee.jar(里面包含了所用的关于mail的API)、jigloo312.zip(提供一个用户可定制GUI的JAVA API可到http://cloudgarden.com/jigloo/index.html下载)
【简介】在介绍本文之前,需要先简介一下邮件服务的一些基本概念,以期更好的理解本文。在一个目前常用的邮件系统中,收发邮件功能的实现是通过不同的协议实现的,收邮件一般采用POP(PostOffice Protocol)协议,即邮局协议,目前所用的版本是3,所以人们通常称之为POP3,。该协议定义了接收邮件的机制,并规定每个用户只能有一个邮箱的支持。占用端口一般为25;发邮件一般采用SMTP (Simple Mail Transfer Protocol)协议,即简单邮件传输协议,它定义了发送电子邮件的机制,通过它程序将和您的公司或因特网服务供应商的(Internet Service Provider's,ISP's)SMTP 服务器通信。SMTP 服务器可将消息中转至接收方 SMTP 服务器,以便最终让用户经由 POP 或 IMAP 获得。占用端口一般为110;通过程序实现这两个协议,我们就可以构建自己企业的邮件收发系统。
【关键字】 JavaMail J2EE GUI Eclipse POP SMTP
还在为没有一个统一,兼容于任何平台的邮件收发系统而着急吗?不用急,这篇文章可以给您敲开一扇开发适合自己企业业务环境的邮件收发系统的大门!
一, 开发准备
从www.eclipse.org下载Eclipse,我所用的版本为3.0.0,大家可以下3.0.2或更高;从http://cloudgarden.com/jigloo/index.html下载jigloo312.zip压缩包,将解压后的plugins和features两个文件夹的内容分别拷贝到eclipse包含的plusins和features文件夹里。
在本地新建一个文件夹(如:d:/java/develop)做为java开发的工作区,然后,在桌面新建一个eclipse.exe的快捷方式,右键点击“属性”,在“目标”栏里输入D:/eclipse/eclipse.exe -vm "G:/Java/jdk1.5.0/bin/javaw.exe" -data "d:/java/develop" , 以后你在eclipse创建的程序都在d:/java/develop这个目录下了。如果还有其他项目的开发,可以在重新创建一个文件夹和eclipse.exe的快捷方式,重新指定工作区,相当于又做了一个全新的Eclipse环境。
二, 环境搭建
A.1 从桌面打开新建的eclipse快捷方式,新建一个java项目:
输入项目名:javamail,然后Next->Finish。
2, 右键点击新建的“javamail”项目,选择“Properties”,选择“Java Build Path”,在“Library”里选择“Add External JARs”,将准备的j2ee.jar文件添加到里面,如图:
好了,所有准备工作都做好了,下面开始开发。
三, 开始开发
A, 发送邮件
A.1 在javamail项目里新建一个Package名为“com.leeyoung.javamail.send”,然后新建一个“Swing main application”:
“Next”->输入类名:SendMailGUI ->“Finish”。
A.2利用可拖拽的可视化面板绘制一个发送邮件的GUI界面(界面功夫这里就不垒述了,后面有关界面的制作也都不作详细说明了,本文重点不在与此。有关GUI的知识请参见相关资料。):
A.3 邮件能够发送成功,关键在于这个“发送”按钮的动作的编写。
A.4 了实现发送的功能,我们新建一个java类:MailSender.java
A.5 放着,备用。首先我们要做一个bean,取名为Mail,用来存储邮件的所有信息,之所以这样做,是为了将邮件的信息都封装到一个java类里,更容易管理,思路更明确,同时也是为了让读者比较容易明白这个功能应该怎样实现。在这个mail bean里我们设置了smtpServer、userName、password、from、to、subject、fileName、body以及popServer、popUser、popPassword几个变量,分别用来存储SMTP服务器地址、邮件的用户名、密码、从何而来要到哪去、邮件标题、附件的名字、正文以及POP服务器的地址、用户和其密码。在Eclipse里完成这个bean很简单:新建一个java类,定义上述变量(String smtpServer,userName,password …),然后选中这些变量,击右键->Source->Generate Getters and Setters …,Eclipse会自动生成这些变量的get/set方法。保存这个类,入正题,编写send的功能代码。
A.6 逐步讲解MailSender.java:
i.定义SMTP服务器上的相关信息:
String smtpServer = sendMail.getSmtpServer();
String to = sendMail.getTo();
String from = sendMail.getFrom();
String subject = sendMail.getSubject();
String body = sendMail.getBody();
String fileName = fileName = sendMail.getFileName();
ii,定义一个Properties对象,并填充它:
Properties props = System.getProperties();
props.setProperty("mail.smtp.host", smtpServer);
props.setProperty("mail.smtp.port", "25");
props.put("mail.smtp.auth", "true");
props.setProperty("mail.smtp.user", username);
iii,创建一个Authenticator的子类CheckAuthenticator,并向Session中注册:
CheckAuthenticator auth = new CheckAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
iv,创建一个Message:
Message msg = new MimeMessage(session);
iiv,创建邮件正题,里面的if (!fileName.equals(""))是做了一个是否包含附件的判断,如果没有,就只把正文做为Message的内容发送出去;如果有,就把附件和正文内容一并发送:
try {
BodyPart bp = new MimeBodyPart();
Multipart mp = new MimeMultipart();
bp.setText(body);//增加正文内容到bodypart
mp.addBodyPart(bp);
if (!fileName.equals("")) {
bp = new MimeBodyPart();
FileDataSource fileSource = new FileDataSource(fileName);
bp.setDataHandler(new DataHandler(fileSource));
bp.setFileName(fileSource.getName());
mp.addBodyPart(bp);
}
msg.setContent(mp);
} catch (Exception e) {
System.err.println("增加邮件附件:" + fileName + "发生错误!" + e);
}
vii,发送
Transport.send(msg);
具体源代码(MailSender.java)如下:
//MailSender.java
/*
* Created on 2005-5-19
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package com.leeyoung.jmail.send;
import java.security.Security;
import java.util.Date;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import com.leeyoung.jmail.bean.Mail;
/**
* @author CYY
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class MailSender {
/**
* @param smtpServer
* @param to
* @param from
* @param subject
* @param body
*/
public void send(Mail sendMail) {
String smtpServer = sendMail.getSmtpServer();
String to = sendMail.getTo();
String from = sendMail.getFrom();
String subject = sendMail.getSubject();
String body = sendMail.getBody();
String fileName = fileName = sendMail.getFileName();
try {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
// -- Get a Properties object --
Properties props = System.getProperties();
props.setProperty("mail.smtp.host", smtpServer);
props.setProperty("mail.smtp.port", "25");
props.put("mail.smtp.auth", "true");
StringTokenizer st = new StringTokenizer(from, "@");
String username = st.nextToken();
props.setProperty("mail.smtp.user", username);
CheckAuthenticator auth = new CheckAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
// -- Create a new message --
Message msg = new MimeMessage(session);
// -- Set the FROM and TO fields --
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(
to, false));
msg.setSubject(subject); //下面不设邮件正文 msg.setText(body),
msg.setSentDate(new Date());
//Judge the mail include a affix,if have ,send it;if none ,send the
// mail body only;
System.out.println("开始增加邮件附件:" + fileName);
try {
BodyPart bp = new MimeBodyPart();
Multipart mp = new MimeMultipart();
bp.setText(body);//增加正文内容到bodypart
mp.addBodyPart(bp);
if (!fileName.equals("")) {
bp = new MimeBodyPart();
FileDataSource fileSource = new FileDataSource(fileName);
bp.setDataHandler(new DataHandler(fileSource));
bp.setFileName(fileSource.getName());
mp.addBodyPart(bp);
}
msg.setContent(mp);
} catch (Exception e) {
System.err.println("增加邮件附件:" + fileName + "发生错误!" + e);
}
// -- send the message --
Transport.send(msg);
System.out.println("Message send OK.");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
A.7为了实现邮件的权限认证,我们需要做一个身份验证的模块CheckAuthenticator,这个类从Authenticator(抽象类)继承而来,要使用 Authenticator,先创建该抽象类的子类,并从getPasswordAuthentication()方法中返回PasswordAuthentication实例。创建完成后,必需向 session 注册该Authenticator。这样,在需要认证的时候,就会通知 Authenticator。您可以通过JOptionPane来弹出窗口,这里使用Input对话框得到密码,具体应用中可以使用JPasswordField来实现(这个不是本文重点,关于Swing的内容请参看相关资料)。
//CheckAuthenticator.java
/*
* Created on 2005-5-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package com.leeyoung.jmail.send;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.swing.JOptionPane;
/**
* @author CYY
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class CheckAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication()
{
String userName, password;
Properties props = System.getProperties();
Session session = Session.getInstance(props);
userName = session.getProperty("mail.smtp.user");
password = JOptionPane.showInputDialog("Enter your password :");
return new PasswordAuthentication(userName,password);
}
}
A.8 回到SendMailGUI.java中,给send这个按钮添加动作实现代码:
private void sendActionPerformed(ActionEvent evt) {
/*add by chaoyang
*
*
* Purpose:This is a funcation model.
*/
System.out.println("开始发送邮件... ...");
Mail sendMail = new Mail();
try{
smtpServer_value = smtpAddress.getText();
to_value = receiverField.getText();
from_value = senderField.getText();
subject_value = subjectField.getText();
affixFileName_value = affixField.getText();
body_value = bodyArea.getText();
if(smtpServer_value!= null && to_value!= null && from_value!=null && subject_value!=null && body_value!=null){
System.out.println(smtpServer_value+to_value+from_value+subject_value+body_value);
sendMail.setSmtpServer(smtpServer_value);
sendMail.setTo(to_value);
sendMail.setFrom(from_value);
sendMail.setSubject(subject_value);
jLabel.setText("从"+from_value+" 发往"+to_value+" 注意输入密码 ...");
if(affixFileName_value!=null){
sendMail.setFileName(affixFileName_value);
}
sendMail.setBody(body_value);
MailSender mailSender = new MailSender();
mailSender.send(sendMail);
jLabel.setText("发送成功!");
}
}catch(Exception e){
e.printStackTrace();
}
}
A.9 至此,这个GUI界面的邮件发送器完整做完了,它可以象在网易邮箱上发送邮件一样发送带附件的邮件。
B, 接收邮件
B.1在javamail项目里新建一个Package名为“com.leeyoung.javamail.receive”,然后新建一个“Swing main application”:
“Next”->输入类名:ReceiveMailGUI ->“Finish”。
B.2 同法炮制,利用可拖拽的可视化面板绘制一个接收邮件的GUI界面:
B.3 对按钮“接收”编写动作实现代码:
private void receiveButtonActionPerformed(ActionEvent evt) {
// reveice your mail:
Mail receiveMail = new Mail();//此处依然用到前面所做的Mail Bean
try{
StringTokenizer st = new StringTokenizer(mailAddressField.getText(),"@");
String popUser = st.nextToken();
String popServer = "pop."+st.nextToken();
popserver.setText("用户"+popUser+" ,您所使用的邮件接收服务器是 :"+popServer);
String popPassword = String.valueOf(passwordField.getPassword());
if(popPassword==null){
JOptionPane.showMessageDialog(null,"您没有输入密码!");
}
if(popServer!=null && popUser!=null && popPassword!=null){
receiveMail.setPopServer(popServer);
receiveMail.setPopUser(popUser);
receiveMail.setPopPassword(popPassword);
//这里用到了一个receive方法,其参数是一个Mail对象,后面会给定义
receive(receiveMail);
}
}catch(Exception e){
e.printStackTrace();
}
}
B.4 “接收”动作实现方法receiveButtonActionPerformed中用到的receive(Mail receiveMail)方法内容如下:
//receive mail Function
/**
* @param popServer
* @param popUser
* @param popPassword
*/
public void receive(Mail receiveMail) {
String popServer = receiveMail.getPopServer();
String popUser = receiveMail.getPopUser();
String popPassword = receiveMail.getPopPassword();
Store store = null;
Folder folder = null;
try {
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
store = session.getStore("pop3");
store.connect(popServer, popUser, popPassword);
folder = store.getDefaultFolder();
if (folder == null)
throw new Exception("No default folder");
folder = folder.getFolder("INBOX");
if (folder == null)
throw new Exception("No POP3 INBOX");
folder.open(Folder.READ_ONLY);
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++) {
printMessage(popServer,msgs[msgNum]);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if (folder != null)
folder.close(false);
if (store != null)
store.close();
} catch (Exception ex2) {
ex2.printStackTrace();
}
}
}
B.5 receive方法中调用的printMessage仅仅是为了将一些邮件信息反映到GUI界面上,这里就不再详解了,代码如下:
/**
* @param message
*/
public void printMessage(String popServer,Message message) {
// TODO Auto-generated method stub
try {
String from = ((InternetAddress) message.getFrom()[0]).getPersonal();
if (from == null)
from = ((InternetAddress) message.getFrom()[0]).getAddress();
statusLable.setText("成功从"+popServer+"接收到邮件!");
mailFrom.setText(from+" <"+String.valueOf(message.getFrom()[0])+">");
String subject = message.getSubject();
this.subject.setText(subject);
Part messagePart = message;
Object content = messagePart.getContent();
if (content instanceof Multipart) {
messagePart = ((Multipart) content).getBodyPart(0);
}
String contentType = messagePart.getContentType();
if (contentType.startsWith("text/plain")
|| contentType.startsWith("text/html")) {
this.contentType.setText("文本");
InputStream is = messagePart.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(is));
String thisLine = reader.readLine();
StringBuffer lineBuffer = new StringBuffer();
while (thisLine != null)
{
System.out.println(thisLine);
lineBuffer.append(thisLine+"/n");
thisLine = reader.readLine();
}
bodyField.setText(lineBuffer.toString());
}
timeLabel.setText(String.valueOf(new Date()));
} catch (Exception ex) {
ex.printStackTrace();
}
}
四, 测试
笔者的环境是WindowsXP sp2+Eclipse3.0.0+jdk1.5.0,所用帐户是chaoyanglee@163.com和imchaoyang@163.com,这两个帐户无论在接收还是发送都能正确无误的执行。
五, 参考
下面介绍一下本文中所用到的重要的API:
·Javax.mail.Session:Session 类定义了一个基本邮件会话(session),是Java Mail API最高层入口类。所有其它类都是经由这个session 才得以生效。Session 对象用 Java.util.Properties 对象获取信息,如邮件服务器、用户名、密码及整个应用程序中共享的其它信息。
·Javax.mail.Message:一旦获得 Session 对象,就可以继续创建要发送的消息。这由 Message 类来完成。因为 Message 是个抽象类,必需用一个子类,多数情况下为 Javax.mail.internet.MimeMessage。MimeMessage 是个能理解 MIME 类型和头的电子邮件消息,正如不同 RFC 中所定义的。虽然在某些头部域非 ASCII 字符也能被译码,但 Message 头只能被限制为用 US-ASCII 字符。
·Javax.mail.Address:一旦您创建了 Session 和 Message,并将内容填入消息后,就可以用 Address 确定信件地址了。和 Message 一样,Address 也是个抽象类。您用的是 Javax.mail.internet.InternetAddress 类。
·Javax.mail. Authenticator:与 Java.net 类一样,JavaMail API 也可以利用 Authenticator 通过用户名和密码访问受保护的资源。对于JavaMail API 来说,这些资源就是邮件服务器。JavaMail Authenticator 在 Javax.mail 包中,而且它和 Java.net 中同名的类 Authenticator 不同。两者并不共享同一个 Authenticator,因为JavaMail API 用于 Java 1.1,它没有 Java.net 类别。 要使用 Authenticator,先创建一个抽象类的子类,并从 getPasswordAuthentication() 方法中返回 PasswordAuthentication 实例。创建完成后,您必需向 session 注册 Authenticator。然后,在需要认证的时候,就会通知 Authenticator。您可以弹出窗口,也可以从配置文件中(虽然没有加密是不安全的)读取用户名和密码,将它们作为 PasswordAuthentication 对象返回给调用程序。
·Javax.mail.Transport:消息发送的最后一部分是使用 Transport 类。这个类用协议指定的语言发送消息(通常是 SMTP)。它是抽象类,它的工作方式与 Session 有些类似。仅调用静态 send() 方法,就能使用类的 缺省 版本:Transport.send(message);或者,读者也可以从针对自己的协议的会话中获得一个特定的实例,传递用户名和密码(如果不必要就不传),发送消息,然后关闭连接。
·Javax.mail.Store:Store类实现特定邮件协议上的读、写、监视、查找等操作。通过Javax.mail.Store类可以访问Javax.mail.Folder类。
·Javax.mail.Folder:Folder类用于分级组织邮件,并提供照Javax.mail.Message格式访问email的能力。