在本章中,我们将要了解到如何在servlet(或者其他任何类的Java应用程序)中发送电子邮件。你可能想要在某个用户第一次在你的站点注册的时候给他发送电子邮件,或者你可能想要发送有关正在运行的servlet的运行统计的消息(想象这么一个servlet,它在servlet被调用的时候负责管理用户,你可能需要每天得到一封当天用户的活动统计,以便对你的用户计费)。也许你需要在你的servlet产生异常或者遇到了严重错误的时候得到一个通知。更进一步,你可能想要创建一个自己的在线的电子邮件系统,通过它可以在Internet上阅读和发送电子邮件。需要发送电子邮件的应用真是不胜枚举。
几乎所有的问题都可以用多种不同的方法解决。在用Java编写的应用程序中发送电子邮件也是如此。
·你可以打开一个连接到邮件服务器的socket,然后使用低层协议如简单报文传送协议(SMTP,Simple Mail TransportProtocol)来进行通信。你将不得不自己来处理协议中所有的细节。这实在是一件复杂而枯燥的工作。
·你可以使用在低层协议基础上的某个免费的电子邮件类。这些类的使用抽象了传输的细节。不过有很多这种免费的电子邮件类都只支持电子邮件的发送,而不支持电子邮件的阅读。
·你还可以用Sun Microsystem的JavaMail API。JavaMail API被设计来给Java应用程序加入电子邮件的能力。
你觉得哪一个方案最好呢?当然,答案还要考虑许多因素,不过,如果你真是想要做许多辛苦的工作并且迫切想要了解传送协议是如何工作,那么你可以选用使用socket连接到邮件服务器的方案。有些人可能会考虑使用一些由别人开发的简单的类来简化这项工作,当然,这样做你会受到这个类所提供的功能的限制,不过这也有可能恰好满足你的需求。如果你想要让你的系统成为Java的电子邮件系统中的卡迪拉克,那么你只能使用JavaMail。JavaMailAPI非常健壮而且覆盖了你可能需要的电子邮件的全部功能。
比较这三种方案,我们可以得到这样的结论:在传输层协议的层次上编程实在是太烦琐了而且容易出错。为什么要将宝贵的时间浪费在已经有人经历了痛苦才实现了的低层协议细节上呢?正如后面所提到的使用简单的类更为简单且合理。Sun在这个版本的JDK中已经提供了这样一个简洁类sun.net.smtp.SmtpClient。由于这个类位于“sun”树中(区别于“java”树),所以它被认为是不被支持的。对于那些要求必须使用官方发布的而且有支持的软件的企业,显然不能使用这个方案。另外,一定要记住,将Java虚拟机从一个平台移植到另外一个平台的开发商,无须移植那些“sun”树中的类。这意味着大部分JVM提供了SmtpClient类,但是不是所有的JVM都支持它。
SmtpClient类实现了简单报文传送协议(SMTP)。要发送一个电子邮件,我们可以实例化一个新的SmtpClient对象,调用to()方法来指定收件人,调用from()方法来定义发件人,然后调用startMessage()方法返回一个输出流,我们可以向这个流中写入消息首部和正文,最后关闭这个SmtpClient,让SmtpClient自己来发送这个消息。
图14.1显示了SendMailServlet的源程序。这个servlet创建一个HTML表单来取得发件人、主题和消息正文。一旦这个表单被提交,servlet使用SmtpClient类发送这个消息。package javaservlets.mail; import javax.servlet.*; import javax.servlet.http.*; import sun.net.smtp.*; /** * <p>This servlet will format an email form in HTML and, when * the user submits the form, will mail the message using * SMTP */ public class SendMailServlet extends HttpServlet { public static String MAIL_FROM = "from"; public static String MAIL_SUBJECT = "subject"; public static String MAIL_BODY = "body"; // Multiple 'to' addresses can be separated by commas public static String MAIL_TO = "karl@servletguru.com"; public static String MAIL_HOST = "server1.electronaut.com"; /** * <p>Performs the HTTP GET operation * * @param req The request from the client * @param resp The response from the servlet */ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { // Set the content type of the response resp.setContentType("text/html"); // Get the PrintWriter to write the response java.io.PrintWriter out = resp.getWriter(); // Create the HTML form out.println("<html>"); out.println("<head>"); out.println("<title>Send Email</title>"); out.println("<center><h2>Send Email to Karl Moss</h2>"); out.println("<br><form method=POST action=\"" + req.getRequestURI() + "\">"); out.println("<table>"); out.println("<tr><td>From:</td>"); out.println("<td><input type=text name=" + MAIL_FROM + " size=30></td></tr>"); out.println("<tr><td>Subject:</td>"); out.println("<td><input type=text name=" + MAIL_SUBJECT + " size=30></td></tr>"); out.println("<tr><td>Text:</td>"); out.println("<td><textarea name=" + MAIL_BODY + " cols=40 rows=6></textarea></td></tr>"); out.println("</table><br>"); out.println("<input type=submit value=\"Send\">"); out.println("<input type=reset value=\"Reset\">"); out.println("</form></center></body></html>"); // Wrap up out.println("</body>"); out.println("</html>"); out.flush(); } /** * <p>Performs the HTTP POST operation * @param req The request from the client * @param resp The response from the servlet */ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { // Set the content type of the response resp.setContentType("text/html"); // Create a PrintWriter to write the response java.io.PrintWriter out = new java.io.PrintWriter(resp.getOutputStream()); // Get the data from the form String from = req.getParameter(MAIL_FROM); String subject = req.getParameter(MAIL_SUBJECT); String body = req.getParameter(MAIL_BODY); try { // Create a new SMTP client SmtpClient mailer = new SmtpClient(MAIL_HOST); // Set the 'from' and 'to' addresses mailer.from(from); mailer.to(MAIL_TO); // Get the PrintStream for writing the rest of the message java.io.PrintStream ps = mailer.startMessage(); // Write out any mail headers ps.println("From: " + from); ps.println("To: " + MAIL_TO); ps.println("Subject: " + subject); // Write out the message body ps.println(body); // Send the message mailer.closeServer(); // Let the user know that the mail was sent out.println("<html>"); out.println("<head>"); out.println("<title>Send Email</title>"); out.println("<body><center>"); out.println("<h2>Your email has been sent!</h2>"); out.println("</center></body></html>"); } catch (Exception ex) { // Got an error sending the mail; notify the client out.println("<html>"); out.println("<head>"); out.println("<title>Send Email Error</title>"); out.println("<body><center>"); out.println("<h2>There was an error sending your email</h2>"); out.println("<br>Message=" + ex.getMessage()); out.println("</center>"); out.println("</body></html>"); } // Wrap up out.flush(); } }图14.1 SendMailServlet代码清单
图14.2所显示的HTML表单是在doGet()方法中创建的,在doPost()方法中,发送了这个消息。在这个servlet中收件人被硬编码,不过你可以很简单的在表单中为它加上一个输入域来获得收件人。你还应该知道如何使用SmtpClient自动发送消息。比如在你的servlet遇到异常的时候,给你自己发送一封电子邮件。
使用SmtpClient类的唯一的问题就是产生邮件首部。这不是由类缺省产生的,相反的,SMTP协议使用邮件首部来发送消息,所以它是必须的。邮件首部必须遵守RFC822。RFC822取代了题为ARPA网络文本消息格式标准的RFC733。新版本RFC的标题是ARPAInternet文本消息格式标准——请注意网络文本消息变成了Internet文本消息。所有的消息首部都在消息规范的语法节中定义,不过大多数语法很少使用。下面是一些比较重要的消息首部:
From:<发件人>
Date:<发件日期>
To:<一个或多个收件人>
cc:<更多收件人>
Subject:<标题>
虽然不要求消息首部一定要按照某种顺序,不过最好使用上述顺序。所有的消息首部都具有“name:vlaue”的格式,比如To:karlmoss@mindspring.com。关于如何使用消息首部的更完整的例子,可以参考RFC822的附录A。如果你的手头没有RFC 822,你可以在Internet上查找它,你会发现有很多站点都提供了RFC。
SmtpClient没有提供阅读邮件的能力。在很多情况下,这不成问题,因为你可能仅仅需要发送消息。不过如果你还想要检查你个人的电子邮件帐户,又该怎么办呢?SMTP并不涉及这个问题,所以我们需要使用一套更健壮的类,比如JavaMail API中定义的类。相对于Java平台JavaMail API是一个新增的部分。从一开始,JavaMail就被设计来使为Java应用程序加入电子邮件能力尽可能的简单。这并不意味着这套API限制了它的功能(就像SmtpClient类)。就像很多现有的消息系统(如MAPI和IMAP)一样,JavaMail非常健壮。JavaMail
API定义了一个用来管理邮件的公共接口,这样第三方厂商就可以提供指定消息系统的替代产品了。JavaMail允许你在程序中使用API定义的接口,这样在运行的过程中,可以请求使用某种特定的实现。
JavaMail API定义的四个主要组件是Message,Folder,Story和Session。Message类定义了一系列属性,诸如地址信息、邮件消息的内容类型等等。所有的消息被保存在文件夹之中,比如我们熟悉的INBOX文件夹。文件夹还可以包含子文件夹,这样可以建立一个树型的层次结构。Folder类定义了取得、复制、追加、删除消息的方法。Store类定义了一个含有文件夹层次结构(包括文件夹中所包含的消息)的数据库。Store类还定义了用来访问文件夹和消息的访问协议类型。Session类负责对用户进行认证以及控制对消息存储和传输的访问。
在应用程序中使用JavaMail来读取邮件的基本流程如下:
1.创建一个新的Session对象并且定义其使用的存储的类型(如IMAP或者POP3)和传输的类型(如SMTP)。
2.使用Session对象,创建一个新的Store对象。在访问消息的存储之前,当前用户必须经过认证。
3.从Store中取得Folder,比如INBOX。
4.从Folder中获取消息。
在应用程序中使用JavaMail来发送邮件的基本流程如下:
1.创建一个新的Session对象并且定义其使用的存储的类型(如IMAP或者POP3)和传输的类型(如SMTP)。
2.使用Session对象,创建一个新的Message对象,并设置首部的一些属性如“from”名字和收件人。
3. 调用消息的Transport.send()方法。这个方法会根据在消息首部中给出的收件人来使用正确的传输发送消息。
在开始使用JavaMail之前,我们先要从网上(http://java.sun.com/products/javamail)下载并安装它的最新的版本。这个发布包括API(在mail.jar中)和存储提供者(如IMAP)以及传输提供者(如SMTP)。别忘了把mail.jar加入到你的servlet引擎CLASSPATH中。
如果你需要使用其他类型的服务(比如POP3发送),你就需要寻找一个可以提供这种特殊服务支持的供应商。现在这里有一个可以提供POP3发送的实用软件,它由Sun公司实现(它也可以在http://java.sun.com/products/javamail上找到)。只要根据提示安装并注意将作为服务器的jar文件加入到你的CLASSPATH中就可以了。
暂且不去了解JavaMail API规范中骇人听闻的细节,我们先来看一个例子。在本章的其余部分,我们将要开发一个叫做SimpleMailReader的servlet。这个servlet打开并且阅读一个邮箱,显示消息(包括多部分的消息),而且允许发送消息。由于这个例子有点长,这里我们只列出了一些关键的方法,完整的源程序可以在本书配套光盘中找到。在阅读邮件之前,必须首先登录。SimpleMailReader使用第6章中提到的会话管理来确定用户是否需要登录。如果要求用户登录,就创建一个HTML表单来取得登录信息(如图14.3所示)。
除了要取得邮件主机、用户名和口令之外,登录表单还要求用户选择传输协议和存储协议。servelt是怎样找出能支持什么协议呢?JavaMailAPI提供了一种方法来取得已经安装了哪些传输协议和存储协议。让我们回忆一下,我们将提供服务的jar文件放到了CLASSPATH中。在jar文件中包含了各个类的实现,而在meta-inf\javamail.providers文件中包含了一个内容描述。这个文件包含了服务的名称、协议(存储还是传输)、实现的类以及开发商名称等信息。POP3存储提供者的javamail.providers文件如图14.4所示。看来在开发商名称中有一些空格实在没有什么道理。
protocol=pop3;type=store;class=com.sun.mail.pop3.POP3Store; vendor=Sun Microsy stems,Inc;一旦用户填写了正确的信息并按下了Login按钮,SimpleMailServlet就会试图用指定的存储协议登录到指定主机上的邮件存储上。图14.5显示了登录所需的代码。
/** * Logs on to the specified Mail store * @param store The store protocol * @param storeHost The store host name * @param transport The transport protocol * @param transportHost The transport host name * @param user The user name * @param password The user password */ public void login(String store, String storeHost, String transport, String transportHost, String user, String password) throws MessagingException { try { // Create a new session close(); java.util.Properties props = System.getProperties(); props.put("mail.host", storeHost); m_session = Session.getDefaultInstance(props, null); // Create a new URLName URLName url = new URLName(store, storeHost, -1, "INBOX", user, password); m_store = m_session.getStore(url); m_store.connect(); m_inbox = m_store.getFolder("INBOX"); m_inbox.open(Folder.READ_WRITE); // Set properties m_storeProtocol = store; m_storeHost = storeHost; m_transportProtocol = transport; m_transportHost = transportHost; m_user = user; m_loggedIn = true; } finally { // If there is some failure make sure that everything // is closed down properly if (!m_loggedIn) { if (m_store != null) { m_store.close(); m_store = null; } } } }
图14.5 登录到邮件存储
<p> 如果登录成功,一个显示了用户INBOX中所有消息的页面就会被创建;否则,向用户返回错误并要求用户再次尝试登录。由于使用了会话管理,所以我们非常简单地就能在每一个用户的会话数据中保持一个计数器来限制用户尝试登录的次数。</p> <h3>14.3.2 用JavaMail阅读邮件</h3> <p> 在登录过程中,我们连接到了邮件存储并且打开了被称为INBOX的文件夹。在打开文件夹之后,我们就可以很容易地取得用户INBOX中的所有消息并显示它们。图14.6实现这些功能的代码。而图14.7显示了一个INBOX页面的例子。 </p>
/** * Shows the all of the messages in the current user's inbox * * @param req The request from the client * @param resp The response from the servlet * @param mailUser The current SimpleMailUser */ public void showInbox(HttpServletRequest req, HttpServletResponse resp, SimpleMailUser mailUser) throws ServletException, java.io.IOException { // Set the content type of the response resp.setContentType("text/html"); // Get the PrintWriter to write the response java.io.PrintWriter out = resp.getWriter(); // Set the response header to force the browser to // load the HTML page from the server instead of // from a cache resp.setHeader("Expires", "Tues, 01 Jan 1980 00:00:00 GMT"); // Get the URI of this request String uri = req.getRequestURI(); // Print a standard header out.println("<html>"); out.println("<head>"); out.println("<title>" + FORM_TITLE + "</title>"); out.println("<body><center><h2>"); out.println("INBOX for " + mailUser.getUser() + " using " + mailUser.getStoreProtocol() + " on host " + mailUser.getStoreHost()); out.println("</h2>"); try { // Get the inbox Folder inbox = mailUser.getInbox(); // Get the number of messages in the inbox int n = inbox.getMessageCount(); // Get the messages from the inbox Message[] msgs = inbox.getMessages(); out.println("Total number of messages: " + n + "<br>"); out.println("<form action=\"" + uri + "\" METHOD=\"POST\">"); out.println("<input type=submit name=" + FORM_ACTION + " value=\"" + ACTION_LOGOFF + "\">"); out.println("<input type=submit name=" + FORM_ACTION + " value=\"" + ACTION_WRITE + "\">"); out.println("</form>"); out.println("<table border>"); out.println("<tr><th></th>"); out.println("<th>From</th>"); out.println("<th>Sent</th>"); out.println("<th>Subject</th></tr>"); // Loop through the inbox for (int i = 0; i < n; i++) { Message m = msgs[i]; // Skip deleted messages if (m.isSet(Flags.Flag.DELETED)) { continue; } out.println("<tr>"); // Give the user somewhere to click to view the // message out.println("<td><a href=\"" + uri + "?" + FORM_ACTION + "=" + ACTION_VIEW + "&" + FORM_MSG + "=" + i + "\">" + ACTION_VIEW + "</a>"); // Show the from address Address from[] = m.getFrom(); Address addr = null; if ((from != null) && (from.length > 0)) { addr = from[0]; } out.println("<td>" + getAddress(addr) + "</td>"); // Show the sent date java.util.Date date = m.getSentDate(); String s = ""; if (date != null) { s = "" + date; } out.println("<td>" + s + "</td>"); // Show the subject s = m.getSubject(); if (s == null) { s = ""; } out.println("<td>" + s + "</td>"); out.println("</tr>"); } out.println("</table>"); } catch (MessagingException ex) { out.println("<br>"); out.println("ERROR: " + ex.getMessage()); } // Wrap up out.println("</center>"); out.println("</body>"); out.println("</html>"); out.flush(); } </pre> 图14.6 从文件夹中获取消息</p> <p> 注意到这里我们只显示了邮件的标题部分——地址、日期和标题。除了这些邮件的标题,还为每一个消息创建了一个链接,当用户选择了某个标题的邮件,这个邮件的内容就可以显示出来。仔细分析这段代码,你可能会奇怪mailUser对象是什么。这个对象保存在servlet的会话中,它包含了用户信息以及连接到邮件存储的连接。SimpleMailUser类实现了HttpSessionBindingListener接口,所以在会话被清除的时候,它会得到一个通知。这样我们就有机会在浏览器会话终止的时候确保邮件存储已经被正确关闭。</p> 使用JavaMail来显示一个消息或多部分组成的消息是十分简单的。JavaMail提供了判断消息或者消息段内容类型的方法,而且允许我们来控制如何显示它们。Plain-text消息最为简单,我们仅仅将这些文本输出到HTML页中中就可以了。但是对于由多个部分组成的消息呢?我们只要简单地列出所有的部分然后找出每一部分的内容类型就可以了。如果这一部分是文本,那么就将它输出HTML页面中;否则,我们就创建一个链接,用户单击这个链接就显示这部分的内容。比如,如果有一个.gif图像附件,那么,用户可以单击这个图像文件的名字,SimpleMailReader就会读出这个图像,设置响应的内容类型,然后将这个图像文件的内容发送到客户端。图14.8显示了阅读消息以及各个消息部分的代码,而图14.9显示了一个由多个部分组成的邮件显示出来的例子。 </p> <pre class="code"> /** * Shows the specified message * * @param req The request from the client * @param resp The response from the servlet * @param mailUser The current SimpleMailUser * @param msg The message number to display */ public void showMessage(HttpServletRequest req, HttpServletResponse resp, SimpleMailUser mailUser, int msg) throws ServletException, java.io.IOException { // Set the content type of the response resp.setContentType("text/html"); // Get the PrintWriter to write the response java.io.PrintWriter out = resp.getWriter(); // Set the response header to force the browser to // load the HTML page from the server instead of // from a cache resp.setHeader("Expires", "Tues, 01 Jan 1980 00:00:00 GMT"); // Get the URI of this request String uri = req.getRequestURI(); // Print a standard header out.println("<html>"); out.println("<head>"); out.println("<title>" + FORM_TITLE + "</title>"); out.println("<body>"); try { // Get the inbox Folder inbox = mailUser.getInbox(); // Get the messages from the inbox Message[] msgs = inbox.getMessages(); // Get the requested message Message m = msgs[msg]; // Show the date out.println("Date: " + m.getSentDate() + "<br>"); // Show the from addresses. Address a[] = m.getFrom(); out.println("From: " + formatAddresses(a) + "<br>"); // Show the to addresses a = m.getRecipients(Message.RecipientType.TO); out.println("To: " + formatAddresses(a) + "<br>"); // Show the copy addresses a = m.getRecipients(Message.RecipientType.CC); out.println("Cc: " + formatAddresses(a) + "<br>"); // Show the subject String s = m.getSubject(); if (s == null) { s = ""; } out.println("Subject: <b>" + s + "</b><br><hr>"); // Display the message Object o = m.getContent(); // Figure out what kind of message we have if (m.isMimeType("text/plain")) { // Plain text message out.println("<pre>" + o + "</pre>"); } else if (m.isMimeType("multipart/*")) { // Multi-part message Multipart mp = (Multipart) o; // Loop through the parts for (int j = 0; j < mp.getCount(); j++) { Part part = mp.getBodyPart(j); // Get the content type of this part String contentType = part.getContentType(); if (contentType == null) { out.println("Bad content type for part " + j); continue; } ContentType ct = new ContentType(contentType); if (j != 0) { out.println("<hr>"); } // Plain text part if (ct.match("text/plain")) { out.println("<pre>" + part.getContent() + "</pre>"); } else { String desc = "Attachment "; s = part.getFileName(); if (s != null) { desc += s; } // Generate a URL for this part out.println("<td><a href=\"" + uri + "?" + FORM_ACTION + "=" + ACTION_VIEW + "&" + FORM_MSG + "=" + msg + "&" + FORM_PART + "=" + j + "\">" + desc + "</a>"); } } } else { // Unknown MIME type out.println(m.getContentType()); } } catch (MessagingException ex) { out.println("<br>"); out.println("ERROR: " + ex.getMessage()); } // Wrap up out.println("</body>"); out.println("</html>"); out.flush(); } /** * Shows the specified part of a message * * @param req The request from the client * @param resp The response from the servlet * @param mailUser The current SimpleMailUser * @param msg The message number to display * @param part The part number to display */ public void showPart(HttpServletRequest req, HttpServletResponse resp, SimpleMailUser mailUser, int msg, int part) throws ServletException, java.io.IOException { // Get the PrintWriter to write the response java.io.PrintWriter out = resp.getWriter(); try { // Get the inbox Folder inbox = mailUser.getInbox(); // Get the messages from the inbox Message[] msgs = inbox.getMessages(); // Get the requested message Message m = msgs[msg]; // Get the requested part Multipart mp = (Multipart) m.getContent(); Part p = mp.getBodyPart(part); // Set the content type String contentType = p.getContentType(); if (contentType == null) { out.println("Invalid message part at " + part); } else { ContentType type = new ContentType(contentType); // Set the content type for the response to the browser resp.setContentType(type.getBaseType()); // Copy the contents of the part to the output // stream java.io.InputStream in = p.getInputStream(); int b; while (true) { b = in.read(); if (b == -1) { break; } out.write(b); } } } catch (MessagingException ex) { out.println("<br>"); out.println("ERROR: " + ex.getMessage()); } out.flush(); }
图14.8 获取消息的内容
我想你马上就会同意用JavaMail发送消息甚至比我们刚才提到的SmtpClient还要容易。我们无须设置消息的首部,也不必了解输出流。图14.10显示了用JavaMail发送消息的代码。一旦创建了一个新的消息对象,设置谁发送了消息、所有的收件人、消息的标题以及消息正文都是很简单的事情。所有这些都完成之后,就使用传输协议将消息发送到赛博空间。
/** * Sends the composed message. Note that the 'To' and 'Cc' * fields can contain multiple addresses separated by * either a comma or space. * * @param req The request from the client * @param resp The response from the servlet * @param mailUser The current SimpleMailUser */ public void sendMessage(HttpServletRequest req, HttpServletResponse resp, SimpleMailUser mailUser) throws ServletException, java.io.IOException { // Set the content type of the response resp.setContentType("text/html"); // Get the PrintWriter to write the response java.io.PrintWriter out = resp.getWriter(); // Create the HTML form out.println("<html>"); out.println("<head>"); out.println("<title>" + FORM_TITLE + "</title>"); out.println("<body>"); // Get the form input fields String from = req.getParameter(FORM_FROM); String to = req.getParameter(FORM_TO); String cc = req.getParameter(FORM_CC); String subject = req.getParameter(FORM_SUBJECT); String msg = req.getParameter(FORM_MSG); // Validate if ((from == null) || (from.trim().length() == 0)) { out.println("'From' name must be given"); } else if ((to == null) || (to.trim().length() == 0)) { out.println("'To' address(es) must be given"); } else { try { // Create a new message Message m = new MimeMessage(mailUser.getSession()); InternetAddress[] iAddr = null; // Set the 'from' address m.setFrom(new InternetAddress(from)); // Save the 'from' address in the mail user object mailUser.setFromName(from); // Set the 'to' address(es) iAddr = InternetAddress.parse(to, false); m.setRecipients(Message.RecipientType.TO, iAddr); // Set the 'cc' address(es) if given if ((cc != null) && (cc.trim().length() > 0)) { iAddr = InternetAddress.parse(cc, false); m.setRecipients(Message.RecipientType.CC, iAddr); } // Set the subject m.setSubject(subject); // Set the message m.setText(msg); // Send the message on it's way! Transport.send(m); out.println("Message sent!"); } catch (Exception ex) { out.println("Unable to send message: " + ex.getMessage()); } } // Wrap up out.println("</body>"); out.println("</html>"); out.flush(); }图14.10 通过JavaMail发送一个消息
图14.11显示了输入消息的表单。这个表单只允许发送纯文本的消息,不过JavaMail还提供了一个简单的接口来发送多个部分组成的消息。
我先要声明,这个SimpleMailReader servlet的功能并不完备,它甚至没有利用JavaMail API的全部功能。就把它留下来的作为习题吧。另外,JavaMail
API还可以实现下列功能:
·删除消息
·转发消息
·创建由多个部分组成的消息
将所有这些功能组织起来成为一个基于servlet的邮件系统并不是一件难事,特别是JavaMail API的使用使一切都变得十分简单。值得注意的另外一点是,不是所有的服务提供商(如POP3服务提供商)都实现了JavaMail API中所定义的所有功能。例如,有些服务提供商可能不允许删除消息,以备处理可能出现的错误。本章主要介绍了如何在Java应用程序中发送电子邮件,在我们的例子中,是让servlet发送电子邮件。我们讨论了发送邮件的不同实现,诸如打开一个socket连接,使用简化的类以及使用JavaMail API。我们还编写了分别使用Sun公司提供的简化类 SmtpClient类发送邮件和用JavaMail API阅读和发送邮件的例子。发送电子邮件使用哪种方式比较好呢?我建议使用最适合你的需求的方法,在很多情况下,SmtpClient类就能够实现你所需的所有功能,而如果需求比较复杂,那么就要使用更健壮的JavaMail API。