问. 从哪里可以学习有关 Internet 邮件的基础知识,它是我高效编写 JavaMail 程序需要知道的?
答:参见我们的 Web 页中提及的 参考图书 中的一本,获得有关 Internet 电子邮件、MIME、SMTP、IMAP 和 POP3 等的背景信息。
问. 如何调试使用 JavaMail API 的应用程序?
答:可通过在代码中调用 Session 对象上的 setDebug(true) 方法来打开调试模式。这将造成在控制台中打印调试消息,包括协议跟踪信息。如果您认为在 JavaMail 中发现了 bug,就将这个跟踪及以下信息发送给我们:重生成问题的测试用例、使用的平台、使用的 JDK 的版本和使用的邮件服务器 (IMAP, SMTP) 的名称和版本。
问. 如何发送带有附件的消息?
答:带有附件的消息采用 MIME 多部分消息来表示,其中第一部分是消息的主体,其他的部分是附件。有大量的例子,它们展示了如何在演示程序中构建这种消息,JavaMail 下载包包含了这些演示程序。
问. 如何阅读带有附件的消息并保存附件?
答:如前面描述,在 MIME 中,带有附件的消息是作为多部分消息表示的。在简单的例子中,Message 对象的 getContent 方法的结果将是一个 MimeMultipart 对象。多部分对象的第一个主体部分将是消息的主文本。其他的主体部分将是附件。msgshow.java 演示程序展示了如何在消息中遍历所有的多部分对象,并提取各个主体部分数据。getDisposition 方法将给你一个提示,指出主体部分是否应该内嵌显示,或者应该将其考虑成附件(但请注意,不是所有的发件人都提供这一信息)。
为了将主体部分中的数据保存到文件(比如),请使用 getInputStream 方法来访问附件内容,并将数据复制到 FileOutputStream。
注意,也有一些更复杂的情形也要处理。例如,一些发件人把主体作为纯文本和 html 发送。通常,这将作为 multipart/alternative 内容(和 MimeMultipart 对象)出现在简单的文本主体部分的位置中。经过数字签名或加密的消息甚至会更复杂。处理所有的这些情形可能具有挑战性。请参考各种 MIME 规范和我们主页上列出的其他 参考资料。
问. 什么是“中断支持”(disconnected support)?
答:支持中断操作的邮件客户端将允许用户访问远程消息存储(比如 IMAP)中消息,缓存这些消息中的一些消息的(部分)到本地,然后断开到服务器的连接。当处在断开连接状态中,邮件客户端可以访问已经缓存的消息,可能也可以删除它们或将它们保存到其他文件夹。当邮件客户端下一次连接到远程消息存储时,本地所做变更会与远程存储同步。同样,中断支持可以允许客户端在断开与服务器连接时“发送”消息,在到服务器的连接可用前,消息会进行排队。也请参阅 RFC1733。
问. 如何使用 JavaMail API 来支持中断操作?
答:JavaMail API 规范定义了一些接口,邮件客户端可以使用这些接口来支持中断操作。我们的 IMAP 提供程序实现了这些接口(UIDFolder 接口)。
问. 我如何使用 JavaMail API 来发送安全的电子邮件?
答:JavaMail API 目前不支持发送或接收安全电子邮件。JavaMail API 的体系结构使得可以在以后很容易地添加这一支持,我们可以添加支持,第三方也可以添加支持。有关当前电子邮件安全标准(S/MIME 和 PGP)的信息,可以从如下站点找到: http://www.imc.org/smime-pgpmime.html。 请浏览我们的 第三方产品 页,获取来自其他供应商的解决方案。
问. writeTo() 方法生成了消息文件,但消息中的一些行既不是数据的规范 MIME 表示(即使用 CRLF 来结束行),又没有使用我的平台的规范行分隔符(例如 UNIX 上的“\n”)。如果我需要这些表示时,如何获得它们当中的任何一种表示?
答:不管是哪种情形,你都将需要创建合适的 FilterOutputStream 对象来传递给 writeTo()。FilterOutputStream 将需要接受特定的一些行,它们具有任何常见终止符,然后写出另外的一些特定行,它们只具有期望的行终止符。下面是这种过滤器的一些例子。NewlineOutputStream 转换到本地平台的行终止符,当将消息写到文件时,它是有用的。CRLFOutputStream 转换到 MIME 规范 CRLF 终止符,当需要规范 MIME 格式时(比如计算数字签名),它是有用的。
问. 我可以使用 JavaMail API 来实现邮件服务器吗?
答:JavaMail API 设计目的不是帮你实现邮件服务器。但是,对你来说,一些实用工具类,比如 MIME 消息解析类,可能是有用的。通常您会发现,JavaMail API 是在“简单”而不是在“强有力”方面出错。对于邮件客户端,那是合适的,但对于邮件服务器,结果可能是不同的。
问. 我可以使用 JavaMail API 在我的邮件服务器上添加新的用户账户、删除用户账户或改变用户账户的密码吗?
答:JavaMail API 不包括任何工具,用于添加、删除或修改用户账户。在这一方面是没有标准的,每个邮件服务器对它的处理是不同的。
问. 为什么 MimeMessage 类没有实现 Serializable,这样我就可以将消息序列化到磁盘,并在以后读回它?
答:JavaMail API 是在现有电子邮件系统上面设计的,并使用了现有的消息格式。对于这样的一些实现,使用 Java 序列化能力既不是必要的,也不是有用的,因此,不能将它作为 JavaMail API 的目标来考虑。
有关序列化 Message 的困难部分是保留某些指针,它们指向 Folder(文件夹)、Store(存储)和 Session(会话)。如果只想保存消息的内容,而不是对象本身,消息的 writeTo 方法将为你完成这一切。如果想根据序列化消息创建整个电子邮件系统,您应该能够编写 Message 等的子类,并在子类中实现 Serializable。
如果想序列化自己的引用了 MimeMessages的其他对象,那么你的对象的 writeObject 方法可以使用 MimeMessage 的 writeTo 方法,对象的 readObject 方法可以使用 MimeMessage 构造函数,该构造函数会得到 InputStream。在构造 MimeMessage 时,你的类将需要提供一个 Session。
问. 我如何编写服务提供程序?
答:请阅读服务提供程序文档,获取一些细节信息。通常,如果想编写 Store 提供程序,那就编写 javax.mail.Store 和 javax.mail.Folder的子类,也可能要编写 javax.mail.Message 及其他一些类的子类。对于 Transport 提供程序,编写 javax.mail.Transport 的子类,也可能需要编写 javax.mail.Message 及其他的一些类的子类。然后在 javamail.providers 注册表中,添加描述提供程序的条目。如果对编写特别的服务提供程序感兴趣,并且它所针对的协议或邮件系统目前没有得到 JavaMail API 实现的支持,请通过 javamail@Sun.COM 联系我们。
问. 我在登录 Microsoft Exchange 服务器时碰到了麻烦,我确信正在使用的用户名和密码是正确的,我做错了什么?
答:当登录 Exchange 时,需要使用比简单登录名更多的用户名。例如,如果你的电子邮件地址是“J.User@server.com”,Windows NT 登录名是“juser”,NT 域名是“dom”,而且 Exahange 邮箱名是“Joe User”,那么在使用 JavaMail 登录时,你将需要使用用户名 “dom\juser\J.User”。
问. 在发送二进制文件前,我如何编码它?当收到它时,我又如何解码它?
答:你不必这样的做!JavaMail 将自动决定合适的编码用于消息部分,然后才发送消息。而且当读取它们时,将自动解码消息部分。getInputStream 方法将返回解码数据。
问. 如果我不需要自己编码和解码附件,我应该何时使用 MimeUtility 方法?
答:在 JavaMail 没有自动处理的情况下,MimeUtility 方法是有用的。经常发生的这样的一种情形是文件名的编码。基本的 MIME spec(规范)不允许按某种方式编码标题参数值(比如文件名参数),该方式与(比如)编码 Subject(主题)标题相同。这限制了参数值,从而限制了文件名到 ASCII。但一些发件人却实际使用 MIME 文本编码来做非 ASCII 文件名的编码工作。想与这种非标准发件人互操作的应用程序可以使用 encodeText 方法来编码文件名,然后调用 MimeBodyPartsetFileName 方法,而且可以使用 decodeText 方法来解码返回的文件名。
问. 尽管 JavaMail 完成了所有的编码和解码工作,但我仍需要手动控制一些主体部分的编码。
答:在少数的场合需要控制编码,这里有几个方法来重写 JavaMail 的默认行为。下面是一个简单的方法。在创建整个消息后,调用 msg.saveChanges(),然后使用像 mbp.setHeader("Content-Transfer-Encoding", "base64") 的语句来强制对指定主体部分做 base64 编码。
另一种办法是编写 MimeBodyPart 的子类,并重写 updateHeaders 方法,让它首先调用 super.updateHeaders(),然后像上面那样设置 Content-Transfer-Encoding 标题。
问. 为什么 JavaMail 没有在非 ASCII 字符集中正确编码和解码文件名?
答:文件名是作为参数存储在 MIME 标题中的。形如 =?ISO-8859-15?B?5OTkLUluZm8ucGRm?= 的编码文件名不是 MIME spec(规范)的一部分。形如 =?A?B?C?= 的文件名是一个完全有效的文件名,而不是一个不正确编码的文件名。JavaMail 没有编码和解码文件名,因为这样做会违反 MIME 规范。
基本的 MIME 规范不允许编码参数。RFC 2231 定义了一种新的方式,使得可以在 MIME 标题中包括编码参数,包括文件名。它与下面的事实方式不兼容:许多应用程序非法编码文件名。支持 RFC 2231 将不允许 JavaMail 与这些现有的程序互操作。据我所知,很少现有的程序支持 RFC 2231。
如果你选择违反 MIME 规范是为了与其他也违反了 MIME的程序互操作,那么 JavaMail 会给您所有需要的工具来完成这件事。
编码文件名的解决方法是简单的:
mbp.setFileName(MimeUtility.encodeText(filename));
解码文件名的解决方法同样简单:
String filename = MimeUtility.decodeText(part.getFi