Java电子邮件

12/3/2006来源:其它邮件服务器软件人气:11795

摘要
在这篇文章中,显示了创建基于 java 的电子邮件应用程序的第一步。如果你想设计你自己的电子邮件客户系统以代替Microsoft Outlook,或设计一个基于Web的电子邮件系统与Hotmail竞争, 这便是你开始的地方。并且对于 Java 邮件不同前景的可能性方面,我们提供了一个新颖的谈话电子邮件客户应用程序。
在JavaMail中你将发现 APIs允许你开发完整功能的电子邮件客户应用程序。“电子邮件客户应用程序”类似Microsoft Outlook的想法:你可以为你自己的Outlook编一个替代程序,但是一个电子邮件客户端根本不必常驻在一个顾客机器上,其实, 它可以是一个 servlet 或在远程服务器上运行的一个 EJB , 提供终端用户通过访问网络浏览器以收发电子邮件。Hotmail的想法 (你也可以编写你自己的Hotmail版本):你可以完全避开用户界面,设计一个能读取信息和发送回复的自动应答器,依据发件人的格式进行自行定制。这便是一个谈话电子邮件客户系统,下面将继续告诉你更多的内容。
现在,我们开始安装并设置 JavaMail软件。
安装
如果你使用 Java 2 平台企业版 ( J2EE ) 1.3 , 你真幸运:它包括 JavaMail,因此没有必要另外安装。然而,如果你正在运行 Java 2 平台标准版 ( J2SE ) 1.1.7 及更高版本, 要使你的应用程序能够收发电子邮件,则应下载并安装下列程序:
· JavaMail
· JavaBeans Activation Framework
安装方法是解压缩下载文件并把包含的jar文件添加到你的类路径中(classpath)。以下是一个项目的类路径(classpath)的例子:
.;C:\Apps\Java\javamail-1.2\mail.jar;C:\Apps\Java\javamail-1.2\mailapi.jar ;C:\Apps\Java\javamail-1.2\pop3.jar;C:\Apps\Java\javamail-1.2\smtp.jar;C:\Apps\Java\jaf-1.0.1\activation.jar
mailapi.jar 文件包含核心 API 类, pop3.jar 和 smtp.jar 文件为各自的邮件协议包含实现方法。(我们不会在这篇文章中使用 imap.jar 文件。)实现方法类似于 JDBC ( Java 数据库连接 ) 驱动程序, 但消息系统并非数据库。至于 mail.jar 文件, 它包含上面的所有jar文件, 因此你可以把类路径(classpath)只设定到 mail.jar 和 activation.jar 文件。
activation.jar 文件允许你通过二进制数据流处理 MIME ( 多用途因特网邮件扩展 )类型,不仅是在plain text部分查找DataHandler类。
作为文字,余下这篇文章不会提供全面的 API ;相反,你将通过实践学习到更多东西。如果涉及较深的 API 信息,请查看在各自的下载包中的 PDF 文件和Javadocs。
一旦你安装了软件,你需要取得一个电子邮件帐号以便运行列在后面的例子,包括你的 ISP 的SMTP(简单邮件传输协议 ) 服务器名和POP (邮局协议 )服务器名, 你的电子邮件帐号登录名,以及你的邮箱密码。图 1 显示了具体需要的一些邮件帐号细节(并不一定是真实邮件账号),你可以通过使用Microsoft Outlook加以理解。

Figure 1. Tony's Internet mail settings

通过SMTP发送电子邮件
第一个例子显示怎样通过SMTP发送一条基本的电子邮件消息。下面, 你可以看到SimpleSender类, 它从命令行取得你的消息细节并调用一个单独的方法——send(...)——传送消息:
package com.lotontech.mail;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
/**
* 一个简单邮件发送类.
*/
public class SimpleSender
{
/**
* Main 方法以发送在命令行给出的消息.
*/
public static void main(String args[])
{
try
{
String smtpServer=args[0];
String to=args[1];
String from=args[2];
String subject=args[3];
String body=args[4];
send(smtpServer, to, from, subject, body);
}
catch (Exception ex)
{
System.out.PRintln("Usage: java com.lotontech.mail.SimpleSender" +" smtpServer toAddress fromAddress subjectText bodyText");
}
System.exit(0);
}
接下来, 运用SimpleSender,与你的邮件设置一样,用你自己的SMTP服务器名代替smtp.myISP.net :
> java com.lotontech.mail.SimpleSender smtp.myISP.net [email protected] [email protected] "Hello" "Just to say Hello."
如果能正常运行,在收到的信息中你将看见与图 2 显示的一样。

Figure 2. Message received from SimpleSender

Send()方法将完完善SimpleSender类。我将首先显示出代码, 然后再详细说明理论:
/**
* "send" 方法发送消息.
*/
public static void send(String smtpServer, String to, String from, String subject, String body)
{
try
{
Properties props = System.getProperties();
// -- 连接一个缺省会话,或新建一个 --
props.put("mail.smtp.host", smtpServer);
session session = Session.getDefaultInstance(props, null);
// -- 创建一个新消息 --
Message msg = new MimeMessage(session);
// -- 设置 FROM 和 TO 域 --
msg.setFrom(new InternetAddress(from));
msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse(to, false));
// --我们也可以包含 CC 收件人 --
// if (cc != null)
// msg.setRecipients(Message.RecipientType.CC
// ,InternetAddress.parse(cc, false));
// -- 设置 subject 和 body 文本 --
msg.setSubject(subject);
msg.setText(body);
// -- 设置其他一些标头信息--
msg.setHeader("X-Mailer", "LOTONtechEmail");
msg.setSentDate(new Date());
// -- 发送消息 --
Transport.send(msg);
System.out.println("Message sent OK.");
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
首先, 注意你正在取得一个邮件会话 ( java.mail.Session ), 没有什么你不能做的东西。在这种情况下你调用 Session.getDefaultInstance (...) 得到一个共享的会话,另外的桌面应用程序可以再使用它;你也可以建立一个全新的会话—通过 Session.getInstance (...) 方法—它将被你的应用程序独占使用。后者能证明电子邮件客户系统基于per-user基础上不互相孤立是十分重要的。例如一个基于Web的电子邮件系统通过servlets实现。
建立一个会话要求你设置一定的属性;至少,如果通过SMTP发送消息你需要 mail.smtp.host属性。在 API文档中有其他的属性描述。
一旦你有了一个会话,就创建了一条消息。在这个例子中, 你设置了消息的发送和接收邮件地址,标题,以及正文,这都来源于命令行。你也设定了一些头信息, 包括日期,并且如果你想你也可以指定 cc 收件人。
最后, 你通过 javax.mail.Transport 类发送消息。如果你想知道它怎么知道关于我们的邮件会话信息,可往回看看消息的构造。
不只是普通文本
在类 javax.mail.Message ( 从 javax.mail.Part 接口继承 )中方法setText(...)使消息包含提供的字符串并使 MIME类型转到text/plain。
你不仅限于plain text,也能通过 setDataHandler()方法发送其他类型内容。在大多数情况你可以将“其他类型内容”理解为文件附件,例如Word文档。对于一些更有趣东西,检查下面发送Java serialized object的代码:
ByteArrayOutputStream byteStream=new ByteArrayOutputStream();
ObjectOutputStream objectStream=new ObjectOutputStream(byteStream);
objectStream.writeObject(theObject);
msg.setDataHandler(new.DataHandler(new.ByteArrayDataSource( byteStream.toByteArray(),"lotontech/javaobject" )));
你不会在 javax.mail.*包结构中找到DataHandler类,因为它属于JavaBeans Activation Framework (JAF)包javax.activation成员,你象 JavaMail一样下载 JAF 分发。JAF 提供一种机制专门处理typed数据内容及internet MIME类型。
如果你确实用上面的代码发送 Java 对象,你将很难定位 ByteArrayDataSource 类,mail.jar和activation.jar都不包括它。可试着到JavaMail demo目录中找找!
至于你最初感兴趣的那些文件附件, 你可以在DataHandler的构造器中创建一个javax.activation.FileDataSource实例。当然, 你不想单独发送一个文件,就可以给它加上一个附件。为此你需要理解mutipart messages概念,因此我现在将在收到的电子邮件上下文中介绍这个概念。
通过POP3接收电子邮件
更早, 我介绍了通过javax.mail.Message实现javax.mail.Part接口。我现在将解释它的消息部分, 它在这个例子是重要的。开始前先看一眼图表3。

Figure 3. UML diagram for the mail.Part interface

图 3 显示在上一例子创建的一条消息既是message又是message part,因为它实现Part接口。对于任何part, 你能取得它的内容 ( 任意Java对象), 并且, 在一条简单的文本消息实例中,对象可以是String。对于一个multipart message, 内容应是Multipart类型, 从这里我们能得到单个的body parts, 它自己实现Part接口。
在实践中,对于SimpleReceiver类通过代码一切都变得明显, 只用三步:首先, 类的定义及main(...) 方法在命令行建立连接细节;第二,receive(...)方法捕获收到的消息;最后,printMessage(...)方法打印每条消息的标头信息和内容。
以下是第一步:
package com.lotontech.mail;
import javax.mail.*;
import javax.mail.internet.*;
import java.util.*;
import java.io.*;
/**
* 一个简单邮件接收类.
*/
public class SimpleReceiver
{
/**
* Main 方法以便从用命令行参数指定的邮件服务器处接收消息
*/
public static void main(String args[])
{
try
{
String popServer=args[0];
String popUser=args[1];
String popPassword=args[2];
receive(popServer, popUser, popPassword);
}
catch (Exception ex)
{
System.out.println("Usage: java com.lotontech.mail.SimpleReceiver" +" popServer popUser popPassword");
}
System.exit(0);
}
后面我将带你进行正确的测试,现在下面是运行它的命令行(记住要用你的邮件设置代替命令行中的参数):
> java com.lotontech.mail.SimpleReceiver pop.myIsp.net myUserName myPassword
receive(...)方法——从main(...)中调用——接着打开你的POP3收件箱接收消息,每次调用printMessage(...)。下面是代码:
/**
* "receive" 方法接收消息并对它们进行处理
*/
public static void receive(String popServer, String popUser , String popPassword)
{
Store store=null;
Folder folder=null;
try
{
// -- 得到缺省会话--
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
// -- 得到一个POP3消息存储设备并与它连接--
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(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();}
}
}
注意你将从会话中获得一个 POP3 消息存储包,然后使用命令行最初提供的邮件设置与它连结。
一旦连结,你获得缺省文件夹的句柄——对文件夹树的根目录有效——在那里,收件箱保存接收的消息。
你作为只读方式打开收件箱;这样你就可以依次读取信息。
你可能想知道你是否能以写方式打开收件箱,你是否可以为接收的消息或从服务器移出的消息进行标记。在我们的例子,你将只能查看消息。
最后,在上面的代码中你应注意当完成时要关闭文件夹和消息存储,在离开printMessage(...)方法后完成这个类。
打印消息
在这部分,将讨论javax.mail.Part接口。
在列在后面的代码中你将看到怎样将消息赋给它的Part接口并把它分派给messagePart变量。对于single-part消息你现在有一些东西打印。
如果对messagePart.getContent()的调用产生一个Multipart实例,你知道你将处理一人multipart消息;在那种情况中,你将取得第一个multipart——通过getBodyPart(0)——并且打印它。
你可以获得消息本身或仅仅是第一个body part,仅当内容是plain text或 HTML时打印它,你可以通过读取InputStream办到这点:
/**
* "printMessage()" 方法打印一条消息.
*/
public static void printMessage(Message message)
{
try
{
// 得到标头信息
String from=((InternetAddress)message.getFrom()[0]).getPersonal();
if (from==null) from=((InternetAddress)message.getFrom()[0]).getAddress();
System.out.println("FROM: "+from);
String subject=message.getSubject();
System.out.println("SUBJECT: "+subject);
// -- 得到消息部分(也就是说消息本身)--
Part messagePart=message;
Object content=messagePart.getContent();
// --如果是multipart消息,就是其第一个body part--
if (content instanceof Multipart)
{
messagePart=((Multipart)content).getBodyPart(0);
System.out.println("[ Multipart Message ]");
}
// -- 得到类型--
String contentType=messagePart.getContentType();
// -- 如果内容是plain文本,我们可以打印它--
System.out.println("CONTENT:"+contentType);
if (contentType.startsWith("text/plain") || contentType.startsWith("text/html"))
{
InputStream is = messagePart.getInputStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(is));
String thisLine=reader.readLine();
while (thisLine!=null)
{
System.out.println(thisLine);
thisLine=reader.readLine();
}
}
System.out.println("-----------------------------");
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
为了简单起见,我们假设消息本身或第一个body part是可打印的。对于任何实际的应用程序,你将考虑依次读取每一部分并作相应的确良操作——根据其类型打印或将其保存到磁盘。
当你从消息存储中取得每一条消息时,你实际上正获得一个小数据包。数据内容使用一种缓冲的方式获得,在你需要时——你可以只下载消息标头。
SimpleReceiver测试驱动
让我们测试一下SimpleReceiver。为了接收一些东西,我们发送了如图4的消息(注意消息由文本和附件组成)。

Figure 4. Test message for SimpleReceiver

以上接收的被看成是multipart消息,文章打印为:
发送人:Tony Loton
标题:Number 1
[Multipart Message]
内容:text/plain;
charset= “ iso-8859-1 ”
附件 1
发件人 Tony Loton.
-----------------------------
谈谈messages
为了增加一点兴趣,并且示范一个JavaMail APIs 的新颖使用, 我现在简短转到我们的谈话电子邮件项目。为了试一试这一应用,你将需要 lotontalk.jar 文件 (提供),并且你将需要在你的classpath中包括它, 如下所示:
set CLASSPATH=%CLASSPATH%; lotontalk.jar
你也需要在SimpleReceiver类中改变两处代码。首先, 在receive(...)方法中替换如下代码:
// -- 得到消息包并处理它们--
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
printMessage(msgs[msgNum]);
}
用:
// --得到消息包并处理它们--
Message[] msgs = folder.getMessages();
for (int msgNum = 0; msgNum < msgs.length; msgNum++)
{
printMessage(msgs[msgNum]);
speakMessage(msgs[msgNum]);
}
现在增加下列新方法, speakMessage(...), 它类似于原来的printMessage () 方法:
/**
* "speakMessage", printMessage()的谈话版本.
*/
public static void speakMessage(Message message)
{
String speech="";
try
{
com.lotontech.talk.LOTONtalk speaker=new com.lotontech.talk.LOTONtalk();
String from=((InternetAddress)message.getFrom()[0]).getPersonal();
if (from==null) from=((InternetAddress)message.getFrom()[0]).getAddress();
speech=speech+"from "+from+", ";
String subject=message.getSubject();
speech=speech+"subject "+subject+", ";
// -- 得到消息部分(也就是说消息本身)--
Part messagePart=message;
Object content=messagePart.getContent();
// -- ...如果是multipart消息,即是其第一个body part--
if (content instanceof Multipart)messagePart=((Multipart)content).getBodyPart(0);
String contentType=messagePart.getContentType();
if (contentType.startsWith("text/plain") || contentType.startsWith("text/html"))
{
InputStream is = messagePart.getInputStream();
BufferedReader reader =new BufferedReader(new InputStreamReader(is));
String thisLine=reader.readLine();
while (thisLine!=null)
{
speech=speech+thisLine+". ";
thisLine=reader.readLine();
}
// -- 谈话 --
speaker.speak(speech,true);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
因为你在发言之前在一个字符串中积累了整个的消息文本,这个解决方案只仅仅合适于小消息。另外地, 当你读它,你可以能用文本的每一行发言。
当然,直观地显示出结果是不可能的, 因此你必须自己试用它。尽管它还不及我想象的一半好,但你可以得到一个大概的感觉。
下面这个小试验,可能在这个例子范围之外,你将发现谈话综合的一些有趣特征:怎么处理数字,并且怎么假定大写的词是缩略词。
更深层次的考虑
我们这里含盖了一些方面,为建立发送接收电子邮件消息的应用程序奠定了坚实的基础。作为一个起点,从你的 Java 应用程序得到和发送消息并不是困难的,是吗?
如果你想深入,应该学习更多的东西。下面是我们含盖的并且你下一步可能要看的纲要。
送消息
我们显示了怎样通过SMTP协议发送single-part消息, 尽管我暗示过怎样发送非文本内容——也就是说一个Java serialized对象——作为single message part。
接下来, 我们显示了multipart消息上下文的Part接口。你可能现在很想将所学知识马上用于发送消息,那就发送一个由普通文本和一个文件附件组成的multipart消息怎么样?
收到消息
我们讲了接收multipart消息,但只能查看。我们收到的消息保存在服务器上,象我们喜欢的一样可以多次重读,直到我们删除他们。我们将怎样做到这一点呢?改变服务器上消息的状态,思考下面一行代码:
message.setFlag ( Flags.Flag.ANSWERED , true );
正如插图所示,我们介绍了将Java serialized对象写进一个电子邮件消息的想法。下面是接收方法的代码:
InputStream is = (InputStream) message.getContent();
ObjectInputStream objectStream=new ObjectInputStream(is);
Object theObject=objectStream.readObject();
谈话电子邮件
谈话电子邮件可以读取到来的消息这一桌面应用程序可能使你感兴趣,但是我有另外的想法。若干 ISPs——至少在英国——提供一种SpeechMail服务允许你在世界任何地方拔一个电话号码并用你的收件箱消息谈话。你能在 http://www.btinternet.com看见我所说的服务。
我不认为这种服务是由 Java 构造的,但是它是可能的——并且讲话听起来十分清晰。
结束语
我希望我为你的能收发电子邮件的Java 应用程序提供了一个好的起点,对于你下一步怎样走提供了一些想法。我想将由基于 Java 的电子邮件客户系统和Webmail站点在未来月份的不断出现来判定这篇文章的实用性!
,