JavaMail了解1

来源:互联网 发布:公式编辑器 mac 破解 编辑:程序博客网 时间:2024/05/21 14:09

一、JavaMail简介


创建邮件一些基本知识
MIME协议:由于网络的迅猛发展,人们早已经不再满足于电子邮件仅仅是用来交换文本信息

,更希望可以使用电子邮件来交换更为丰富多彩的内容,比如说图片。但是,由于这些内容,

比如说图片,都是非ASCII码的二进制数据,而我们原来的邮箱格式,只是用于表达纯文本信

息,所以要发送这些内容的话,必须先将其进行编码。为了解决这个问题,人们提出了MIME

协议。

MIME协议用于定义复杂邮件的格式,它可以表达多段平行的文本内容和非文本内容。例如,

在邮件中插入附件。

 

JavaMail API封装在一个名为mail.jar的文件中,它是开发JavaMail应用程序时所必须使用的核

心包。

JavaMail API按其功能划分通常可以分为三类:

    创建和解析邮件内容的API。

    发送邮件的API。

    接收邮件的API。

核心类:

1、 Message:javax.mail.Message是创建和解析邮件的核心类,他的实例代表一封电子邮件

。客户端程序发送邮件时,首先使用创建邮件JavaMail API创建出封装了邮件数据的Message

对象,然后把这个对象传送给发送API发送。客户端接收邮件时,邮件接收API把收到的邮件数

据封装在Message类的实例对象中,客户端程序再使用邮件分析API从这个对象中介析出接收

到的邮件数据。

2、 Transport:javax.mail.Transport类是发送邮件的核心API,他的实例对象代表实现了某个

邮件发送协议的邮件发送对象,例如SMTP,客户端程序创建好Message对象后,只需要使用

邮件发送API得到Transport对象,然后把Message对象传递给Transport对象,并调用它的发送

方法,就可以把邮件发送给指定了SMTP服务器。

3 、Store:javax.mail.Store类是接收邮件的核心,他的实例对象代表实现了某个邮件接收协

议的邮件接收对象,例如POP3。客户端程序接收到邮件时,只需要有事邮件接收API得到Store

对象的接收方法,就可以从制定的POP3服务器获得邮件数据,并把这些数据封装带表示邮件

的Message的对象中。

4 、Session:javax.mail.Session类用于定义整个应用程序所需的环境信息,以及收集客户端与

邮件服务器建立网络联接的会话信息,如邮件服务器的主机名,端口号,采用的邮件发送和接

收协议等。Session对象根据信息构建用于邮件收发的Transport和Store对象,以及为客户端创

建Message对象是提供信息支持。

JAF简介

JAF(JavaBeansActivationFramework,JavaBeans激活框架)是一个专用的数据处理框架,他

用于封装数据,并为应用程序提供访问和操作数据的接口。JavaMail API可以利用JAF从某种数

据源中读取数据和获知数据的Mime类型,并用这些数据生成MIME消息中的消息体和消息类型

JAF的主要作用在于让Java应用程序知道如何对一个数据源进行察看,编辑和打印等操作。大

家知道,在MS Word程序中可以打开一片Word文档中嵌套的Visio图片,JAF就是要让Java程序

能够完成类似的技术应用,让应用程序知道数据源支持哪些操作,每个操作分别调用哪个处理

模块。对于通过JAF封装的数据,应用程序通过JAF提供的接口可以完成以下操作:

访问数据源中的数据;

获知数据源的数据类型;

获知可对数据进行的各种操作;

用户对数据执行某种操作时,自动创建执行该操作的软件部件的实例对象。

例如,如果要使用JAF处理一个图片文件,那么应用程序通过JAF提供的接口就可以得到队图片

文件进行操作的输入输出流,图片文件的MIME类型,JAF为图片文件提供的操作方法(如查看

,毕激活打印图片等等),应用程序调用这些操作时,JAF就会调用相应的处理模块对数据进

行处理。

所以要发邮件除了要导mail.jar文件还要导activation.jar文件

创建邮件基本原理:

JavaMail API使用javax.mail.Message类来表示一封邮件,Message类是一个抽象类,应用程序

需要使用其实现子类java.mail.internet.MimeMessage来创建Message类的实例对象。如果要创

建一封致包含文本的简单邮件,那么只需要使用到MimeMessage类就可以了。但如果要创建

一封包含内嵌资源的或多个附件的复杂邮件,那么则需要同时使用到JavaMail API中的

MimeMessage,javax.mail.internet.MimeBodyPart和javax.mail.internet.MimeMultipart等类。

    MimeMessage类:表示整封邮件。

    MimeBodyPart类:表示邮件的一个MIME消息。

    MimeMultipart类:表示一个由多个MIME消息组合成的MIME消息。

简单的一些说明:

    1 复杂邮件和简单邮件一样,都是使用MimeMessage队向来表示整封邮件的。

    2 对于复杂邮件的邮件体,不能再简单的调用MimeMessage.setText方法生成,而是要用一

个MimeMultipart对象表示,并通过MimeMessage.setContent(Multipart mp)方法设置到

MimeMessage对象。

    3 MimeMultipart对象用来组合多个MIME消息,其中的每个MIME消息分别用一个

MimeMultipart.addBodyPart(BodyPart part)方法加入到MimeMultipart对象中。

    4 如果MimeBodyPart对象表示的MIME消息又是一个MIME组合消息,那么它的内容有需要

使用一个MimeMultipart对象来表示,并通过MimeBodyPart.setContent(Multipart mp)方法设置

到MimeBodyPart对象中。与代表整封邮件的MimeMultipart对象一样,这个MimeMultipart对象

中的每一个组成单元也分别有一个MimeBodyPart对象表示。

 

Message.RecipientType内部类:表示收件人的类型,它是Message中的一个静态类。该类中有

如下三个常量:1、 TO:代表邮件的主要接收者。

    2、 CC:代表邮件的抄送接收者。

    3、 BCC:代表邮件的暗送接收者。

   setForm方法:用于设置MimeMessage对象中的发件人地址

   setRecipient和setRecipients方法:分别用于设置一个和一组收件人的地址。

   addRecipient和addRecipients方法:分别用于增加一个和一组收件人的地址。

   setSentDate方法:用于设置MimeMessage对象中的邮件发送日期,

        语法格式如下:

   Public abstract void setSentDate(java.util.Date date)

   setSubject方法:用于设置MimeMessage对象中的邮件主题

     注意:如果邮件主题中含有非ASCII码的字符,那么在subject头字段中需要对邮件主题的

原始内容进行BASE64 或 Quote-Printable编码。

   addHeader和setHeader方法:用于设置当前的MIME消息的某个消息头信息。

   setContent方法:用于设置MimeMessage队向的邮件体

   setText方法:用于将MIME消息体设置为一个简单的纯文本信息。

   setFlag方法:主要用在邮件接收程序中,用于为邮件设置一个邮件标记,例如删除标记,已

读标记等等。

   saveChanges方法:用于更新并保存MimeMessage对象中的数据

   writeTo方法:用于把MimeMessage对象中的数据输出到一个RFC822格式的输出流中

 

一封MIME邮件中的MIME消息可以有三种组合关系:混合,关联,选择。对应的MIME类型如

下:

Multipart/mixed:表示消息体中的内容是混合组合类型,可以是文本,声音和附件等不同邮件内

容的混合体。

Multipart/related:表示消息体中的内容是关联(依赖)组合类型,表示消息体中的某些内容,

依赖另外的资源。

Multipart/alternative:选择组合类型。

 


二、JavaMail详解

 

一、JavaMail概述:
       JavaMail是由Sun定义的一套收发电子邮件的API,不同的厂商可以提供自己的实现类。但

它并没有包含在JDK中,而是作为JavaEE的一部分。
       厂商所提供的JavaMail服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括:
l         SMTP:简单邮件传输协议,用于发送电子邮件的传输协议;
l         POP3:用于接收电子邮件的标准协议;
l         IMAP:互联网消息协议,是POP3的替代协议。
这三种协议都有对应SSL加密传输的协议,分别是SMTPS,POP3S和IMAPS。
除JavaMail服务提供程序之外,JavaMail还需要JAF(JavaBeans Activation Framework)来处理

不是纯文本的邮件内容,这包括MIME(多用途互联网邮件扩展)、URL页面和文件附件等内

容。下图描述了JavaMail的体系结构。

mail.jar:此JAR文件包含JavaMail API和Sun提供的SMTP、IMAP和POP3服务提供程序;
activation.jar:此JAR文件包含JAF API和Sun的实现。

二、对相关协议的回顾:
       1、介绍
       在研究 JavaMail API 的细则之前,让我们回顾用于 API 的协议。基本上,您会逐渐熟悉

并喜爱的协议有四个:
    * SMTP
    * POP
    * IMAP
    * MIME

您还将碰到 NNTP 和其它协议。理解所有协议的基本知识将有助于您理解如何使用 JavaMail

API。虽然不了解这些协议您照样可以用这个 API,却不能够克服那些基础协议的局限性。如

果我们精选的协议不能支持某种性能,JavaMail API 决不能魔术般的将这种性能添加上去。(

您很快就会看到,在处理 POP 时这将成为一个难题。)      

       2、SMTP
       简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)由 RFC 821 定义。它定义了

发送电子邮件的机制。在 JavaMail API 环境中,您基于 JavaMail 的程序将和您的公司或因特

网服务供应商的(Internet Service Provider's,ISP's)SMTP 服务器通信。SMTP 服务器会中

转消息给接收方 SMTP 服务器以便最终让用户经由 POP 或 IMAP 获得。这不是要求 SMTP 服

务器成为开放的中继,尽管 SMTP 服务器支持身份验证,不过还是得确保它的配置正确。像配

置服务器来中继消息或添加删除邮件账号这类任务的实现,JavaMail API 中并不支持。

       3、POP
       POP 代表邮局协议(Post Office Protocol)。目前用的是版本 3,也称 POP3,RFC 1939

定义了这个协议。POP 是一种机制,因特网上大多数人用它得到邮件。它规定每个用户一个邮

箱的支持。这就是它所能做的,而这也造成了许多混淆。使用 POP 时,用户熟悉的许多性能

并不是由 POP 协议支持的,如查看有几封新邮件消息这一性能。这些性能内建于如 Eudora 或

Microsoft Outlook 之类的程序中,它们能记住一些事,诸如最近一次收到的邮件,还能计算出

有多少是新的。所以当使用 JavaMail API 时,如果您想要这类信息,您就必须自己算。

       4、IMAP
       IMAP 是更高级的用于接收消息的协议。在 RFC 2060 中被定义,IMAP 代表因特网消息

访问协议(Internet Message Access Protocol),目前用的是版本 4,也称 IMAP4。在用到

IMAP 时,邮件服务器必需支持这个协议。不能仅仅把使用 POP 的程序用于 IMAP,并指望它

支持 IMAP 所有性能。假设邮件服务器支持 IMAP,基于 JavaMail 的程序可以利用这种情况

— 用户在服务器上有多个文件夹(folder),并且这些文件夹可以被多个用户共享。

       因为有这一更高级的性能,您也许会认为所有用户都会使用 IMAP。事实并不是这样。要

求服务器接收新消息,在用户请求时发送到用户手中,还要在每个用户的多个文件夹中维护消

息。这样虽然能将消息集中备份,但随着用户长期的邮件夹越来越大,到磁盘空间耗尽时,每

个用户都会受到损失。使用 POP,就能卸载邮件服务器上保存的消息了。
    
       5、MIME
       MIME 代表多用途因特网邮件扩展标准(Multipurpose Internet Mail Extensions)。它不

是邮件传输协议。但对传输内容的消息、附件及其它的内容定义了格式。这里有很多不同的有

效文档:RFC 822、RFC 2045、RFC 2046 和 RFC 2047。作为一个 JavaMail API 的用户,您

通常不必对这些格式操心。无论如何,一定存在这些格式而且程序会用到它。

       6、NNTP及其他
       因为 JavaMail API 将供应商和所有其它的东西分开了,您就能轻松添加额外的协议支持

。Sun 保留了一张第三方供应商列表,他们利用了 Sun 不提供超出(out-of-the-box)支持范

围的协议。您会找到 NNTP(网络新闻传输协议)[新闻组]、S/MIME(安全多用途因特网邮件

扩展)及其它支持。
     

三、JavaMail的关键对象:
       JavaMail对收发邮件进行了高级的抽象,形成了一些关键的的接口和类,它们构成了程序

的基础,下面我们分别来了解一下这些最常见的对象。

Properties:属性对象
       由于JavaMail需要和邮件服务器进行通信,这就要求程序提供许多诸如服务器地址、端口

、用户名、密码等信息,JavaMail通过Properties对象封装这些属性西信息。如下面的代码封装

了两个属性信息:

       Properties props = new Properties();
    props.put("mail.smtp.host", "smtp.sina.com.cn");
    props.put("mail.smtp.auth", "true");   

    针对不同的的邮件协议,JavaMail规定了服务提供者必须支持一系列属性,下表是针对

SMTP协议的一些常见属性(属性值都以String类型进行设置,属性类型栏仅表示属性是如何被

解析的):

属性名                                      属性类型        说明
mail.stmp.host                            String            SMTP服务器地址,如smtp.sina.com.cn
mail.stmp.port                            int                 SMTP服务器端口号,默认为25
mail.stmp.auth                            boolean          SMTP服务器是否需要用户认证,默认为

false
mail.stmp.user                             String             SMTP默认的登陆用户名
mail.stmp.from                            String             默认的邮件发送源地址
mail.stmp.socketFactory.class         String             socket工厂类类名,通过设置该属性可以

覆盖提供者默认的实现,必须实现javax.net.SocketFactory接口
mail.stmp.socketFactory.port          int                指定socket工厂类所用的端口号,如果没

有规定,则使用默认的端口号
mail.smtp.socketFactory.fallback     boolean         设置为true时,当使用指定的socket类创建

socket失败后,将使用java.net.Socket创建socket,默认为true
mail.stmp.timeout                        int                  I/O连接超时时间,单位为毫秒,默认为

永不超时

       其他几个协议也有类似的一系列属性,如POP3的mail.pop3.host、mail.pop3.port以及

IMAP的mail.imap.host、mail.imap.port等。更详细的信息请查看com.sun.mail.smtp、

com.sun.mail.pop3和com.sun.mail.imap这三个包的Javadoc:

http://java.sun.com/products/javamail/javadocs/index.html。

Session:会话对象
       Session是一个很容易被误解的类,这归咎于混淆视听的类名。千万不要以为这里的

Session像HttpSession一样代表真实的交互会话,但创建Session对象时,并没有对应的物理连

接,它只不过是一对配置信息的集合。Session的主要作用包括两个方面:

       1)接收各种配置属性信息:通过Properties对象设置的属性信息;
       2)初始化JavaMail环境:根据JavaMail的配置文件,初始化JavaMail环境,以便通过

Session对象创建其他重要类的实例。

       所以,如果把Session更名为Configure也许更容易理解一些。JavaMail提供者在Jar包的

META-INF目录下,通过以下文件提供了基本配置信息,以便session能够根据这个配置文件加

载提供者的实现类:

l         javamail.providers和javamail.default.providers;
l         javamail.address.map和javamail.default.address.map。

       下面是Sun提供者java.mail.default.providers文件的配置信息(位于mail.jar中):
    # JavaMail IMAP provider Sun Microsystems, Inc
    protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Sun

Microsystems, Inc;

    protocol=imaps; type=store; class=com.sun.mail.imap.IMAPSSLStore; vendor=Sun

Microsystems, Inc;

    # JavaMail SMTP provider Sun Microsystems, Inc

    protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Sun

Microsystems, Inc;

    protocol=smtps; type=transport;    class=com.sun.mail.smtp.SMTPSSLTransport;

vendor=Sun Microsystems, Inc;

    # JavaMail POP3 provider Sun Microsystems, Inc

    protocol=pop3; type=store; class=com.sun.mail.pop3.POP3Store; vendor=Sun

Microsystems, Inc;

    protocol=pop3s; type=store; class=com.sun.mail.pop3.POP3SSLStore; vendor=Sun

Microsystems, Inc;

       这个配置文件提供了以下四个方面的信息:
       protocol:协议名称;
       type:协议类型;
       class:对应该操作类型的实现类;
       vendor:厂商名称。
       Session在加载配置文件时会按照以下优先级顺序进行:

       1)首先使用<JAVA_HOME>/lib中的javamail.providers;
       2)如果1)不存在相应的配置文件,使用类路径下mail.jar中META-INF目录下的

javamail.providers;
       3)如果2)不存在相应的配置文件,使用类路径下的mail.jar中META-INF目录下的

javamail.default.providers;

       所以开发者可以在<JAVA_HOME>/lib目录下提供配置文件覆盖mail.jar/META-INF目录中

厂商的配置。但是,一般情况下,我们无须这样做。

       Session通过JavaMail配置文件以及程序中设置的Properties对象构建一个邮件处理环境,

后续的处理将在Session基础上进行。Session拥有多个静态工厂方法用于创建Session实例。

l         static Session getDefaultInstance(Properties props, Authenticator authenticator):当

JVM中已经存在默认的Session实例中,直接返回这个实例,否则创建一个新的Session实例,

并将其作为JVM中默认Session实例。这个API很诡异,我们将对它进行详细的讲解。由于这个

默认Session实例可以被同一个JVM所有的代码访问到,而Session中本身又可能包括密码、用

户名等敏感信息在内的所有属性信息,所以后续调用也必须传入和第一次相同的Authenticator

实例,否则将抛出java.lang.SecurityException异常。如果第一次调用时Authenticator入参为

null,则后续调用通过null的Authenticator入参或直接使用getDefaultInstance(Properties

props)即可返回这个默认的Session实例。值得一提的是,虽然后续调用也会传入Properties,

但新属性并不会起作用,如果希望采用新的属性值,则可以通过getDefaultInstance(Properties

props)创建一个新的Session实例达到目的。Authenticator在这里承当了两个功能:首先,对

JVM中默认Session实例进行认证保护,后续调用执行getDefaultInstance(Properties props,

Authenticator authenticator)方法时必须和第一次一样;其次,在具体和邮件服务器交互时,

又作为认证的信息;

l         static Session getDefaultInstance(Properties props):返回JVM中默认的Session实例,

如果第一次创建Session未指定Authenticator入参,后续调用可以使用该访问获取Session;

l         static Session getInstance(Properties props, Authenticator authenticator):创建一个

新的Session实例,它不会在JVM中被作为默认实例共享;

l         static Session getInstance(Properties props):根据相关属性创建一个新的Session实例

,未使用安全认证信息;

       Session是JavaMail提供者配置文件以及设置属性信息的“容器”,Session本身不会和邮件

服务器进行任何的通信。所以在一般情况下,我们仅需要通过getDefaultInstance()获取一个共

享的Session实例就可以了,下面的代码创建了一个Session实例:

       Properties props = System.getProperties();
    props.setProperty("mail.transport.protocol", "smtp");
           …
    Session session = Session.getDefaultInstance(props);

 

Transport和Store:传输和存储
       邮件操作只有发送或接收两种处理方式,JavaMail将这两种不同操作描述为传输

(javax.mail.Transport)和存储(javax.mail.Store),传输对应邮件的发送,而存储对应邮件

的接收。

       Session提供了几个用于创建Transport和Store实例的方法,在具体讲解这些方法之前,

我们事先了解一下Session创建Transport和Store的内部机制。我们知道提供者在

javamail.providers配置文件中为每一种支持的邮件协议定义了实现类,Session根据协议类型

(stmp、pop3等)和邮件操作方式(传输和存储)这两个信息就可以定位到一个实例类上。

比如,指定stmp协议和transport类型后,Session就会使用com.sun.mail.smtp.SMTPTransport

实现类创建一个Transport实例,而指定pop3协议和store类型时,则会使用

com.sun.mail.pop3.POP3Store实例类创建一个Store实例。Session提供了多个重载的

getTransport()和getStore()方法,这些方法将根据Session中Properties属性设置情况进行工作

,影响这两套方法工作的属性包括:

属性名                                       说明
mail.transport.protocol                  默认的邮件传输协议,例如,smtp
mail.store.protocol                       默认的存储邮件协议,例如:pop3
mail.host                                    默认的邮件服务地址,例如:192.168.67.1
mail.user                                    默认的登陆用户名,例如:zapldy

下面,我们再回头来了解Session的getTransport()和getStore()的重载方法。

l         Transport getTransport():当Session实例设置了mail.transport.protocol属性时,该方

法返回对应的Transport实例,否则抛出javax.mail.NoSuchProviderException。

l         Transport getTransport(String protocol):如果Session没有设置mail.transport.protocol

属性,可以通过该方法返回指定类型的Transport,如transport = session.getTransport(“smtp

”)。

如果Session中未包含Authenticator,以上两方法创建的Transport实例和邮件服务器交互时必

须显示提供用户名/密码的认证信息。如果Authenticator非空,则可以在和邮件服务器交互时

被作为认证信息使用。除了以上两种提供认证信息的方式外,Session还可以使用以下的方法

为Transport提供认证信息。

Transport getTransport(URLName url):用户可以通过URLName入参指定邮件协议、邮件服

务器、端口、用户名和密码信息,请看下面的代码:

       URLName urln = new URLName(“smtp”, “smtp.sina.com.cn”, 25, null, “masterspring2”,

“spring”);
       Transport transport = session.getTransport(urln);

       这里,指定了邮件协议为smtp,邮件服务器是smtp.sina.com.cn,端口为25,用户名/密

码为masterspring2/spring。      

       消息发送的最后一部分是使用  Transport 类。这个类用协议指定的语言发送消息(通常

是 SMTP)。它是抽象类,它的工作方式与 Session 有些类似。仅调用静态 send() 方法,就

能使用类的 缺省 版本:

Transport.send(message);

或者,您也可以从针对您的协议的会话中获得一个特定的实例,传递用户名和密码(如果不必

要就不传),发送消息,然后关闭连接。

message.saveChanges(); // implicit with send()
Transport transport = session.getTransport("smtp");
transport.connect(host, username, password);
transport.sendMessage(message, message.getAllRecipients());
transport.close();

后面这种方法在您要发送多条消息时最好,因为它能保持邮件服务器在消息间的活动状态。基

本 send() 机制为每个方法的调用设置与服务器独立的连接。

       注意:要观察传到邮件服务器上的邮件命令,请用 session.setDebug(true) 设置调试标志


       用 Session 获取消息与发送消息开始很相似。但是,在 session 得到后,很可能使用用户

名和密码或使用 Authenticator 连接到一个 Store。类似于 Transport ,您告知 Store 使用什么

协议:

// Store store = session.getStore("imap");
Store store = session.getStore("pop3");
store.connect(host, username, password);

连接到 Store 之后,接下来,您就可以获取一个 Folder,您必需先打开它,然后才能读里面的

消息。

Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();

POP3 唯一可以用的文件夹是 INBOX。如果使用 IMAP,还可以用其它文件夹。

注意:Sun 的供应商有意变得聪明。虽然 Message message[] = folder.getMessages(); 看上

去是个很慢的操作,它从服务器上读取每一条消息,但仅在你实际需要消息的一部分时,消息

的内容才会被检索。

一旦有了要读的 Message,您可以用 getContent() 来获取其内容,或者用 writeTo() 将内容写

入流。getContent() 方法只能得到消息内容,而 writeTo() 的输出却包含消息头。

System.out.println(((MimeMessage)message).getContent());

一旦读完邮件,要关闭与 folder 和 store 的连接。

folder.close(aBoolean);
store.close();

传递给 folder 的 close() 方法的 boolean 表示是否清除已删除的消息从而更新 folder。

      
Message:消息对象

       一旦获得 Session 对象,就可以继续创建要发送的消息。这由 Message 类来完成。因为

Message 是个抽象类,您必需用一个子类,多数情况下为 javax.mail.internet.MimeMessage

。MimeMessage 是个能理解 MIME 类型和头的电子邮件消息,正如不同 RFC 中所定义的。虽

然在某些头部域非 ASCII 字符也能被译码,但 Message 头只能被限制为用 US-ASCII 字符。

要创建一个 Message,请将 Session 对象传递给 MimeMessage 构造器:
MimeMessage message = new MimeMessage(session);
注意:还存在其它构造器,如用按 RFC822 格式的输入流来创建消息。

 
一旦获得消息,您就可以设置各个部分,因为 Message 实现 Part 接口(且 MimeMessage 实

现 MimePart )。设置内容的基本机制是 setContent() 方法,同时使用参数,分别代表内容和

mime 类型:
message.setContent("Hello", "text/plain");

但如果,您知道您在使用 MimeMessage,而且消息是纯文本格式,您就可以用 setText() 方

法,它只需要代表实际内容的参数,( MIME 类型缺省为 text/plain):
message.setText("Hello");

后一种格式是设置纯文本消息内容的首选机制。至于发送其它类型的消息,如 HTML 文件格式

的消息,我们首选前者。

用 setSubject() 方法设置 subject(主题):
message.setSubject("First");

下面的代码演示了创建一个简单邮件信息的过程:
Message msg = new MimeMessage(session);
msg.setSubject("Test Title");
msg.setText("How are you!");
msg.setSentDate(new Date());
 

Address:地址
       一旦您创建了 Session 和 Message,并将内容填入消息后,就可以用 Address 确定信件

地址了。和 Message 一样,Address 也是个抽象类。您用的是

javax.mail.internet.InternetAddress 类。

若创建的地址只包含电子邮件地址,只要传递电子邮件地址到构造器就行了。

Address address = new InternetAddress("president@whitehouse.gov");

若希望名字紧挨着电子邮件显示,也可以把它传递给构造器:

 Address address = new InternetAddress("president@whitehouse.gov", "George Bush");

需要为消息的 from 域和 to 域创建地址对象。除非邮件服务器阻止,没什么能阻止你发送一

段看上去是来自任何人的消息。

一旦创建了 address(地址),将它们与消息连接的方法有两种。如果要识别发件人,您可以

用 setFrom() 和 setReplyTo() 方法。

message.setFrom(address)

需要消息显示多个 from 地址,可以使用 addFrom() 方法:

Address address[] = ...;
message.addFrom(address);

若要识别消息 recipient(收件人),您可以使用 addRecipient() 方法。除 address(地址)外

,这一方法还请求一个 Message.RecipientType。
message.addRecipient(type, address)

三种预定义的地址类型是:
Message.RecipientType.TO
Message.RecipientType.CC
Message.RecipientType.BCC

如果消息是发给副总统的,同时发送一个副本(carbon copy)给总统夫人,以下做法比较恰当:

Address toAddress = new InternetAddress("vice.president@whitehouse.gov");
Address ccAddress = new InternetAddress("first.lady@whitehouse.gov");
message.addRecipient(Message.RecipientType.TO, toAddress);
message.addRecipient(Message.RecipientType.CC, ccAddress);

JavaMail API 没有提供电子邮件地址有效性核查机制。虽然通过编程,自己能够扫描有效字符

(如 RFC 822 中定义的)或验证邮件交换(mail exchange,MX)记录,但这些功能不属于

JavaMail API。

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 对象返回

给调用程序。

Properties props = new Properties();
// fill props with any information
Authenticator auth = new MyAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
 

发送消息:
       发送电子邮件消息这一过程包括获取一个会话,创建并填充一则消息,然后发送。得到

Session 时,经由设置传递的 Properties 对象中的 mail.smtp.host 属性,可以指定您的 SMTP

服务器:

String host = ...;
String from = ...;
String to = ...;

// Get system properties
Properties props = System.getProperties();

// Setup mail server
props.put("mail.smtp.host", host);

// Get session
Session session = Session.getDefaultInstance(props, null);

 // Define message
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
  new InternetAddress(to));
  message.setSubject("Hello JavaMail");
  message.setText("Welcome to JavaMail");

  // Send message
  Transport.send(message); 

您应该将代码放在一个 try-catch 程序块中,这样创建和发送消息时就能够抛出异常。

消息的提取:
       为读邮件,您获取一个会话,获取并连接一个用于邮箱的适宜的存储(store),打开适

宜的文件夹,然后获取您的消息。同样,切记完成后关闭连接。

  String host = ...;
  String username = ...;
  String password = ...;

  // Create empty properties
  Properties props = new Properties();

  // Get session
  Session session = Session.getDefaultInstance(props, null);

 
  // Get the store
  Store store = session.getStore("pop3");
  store.connect(host, username, password);

  // Get folder
  Folder folder = store.getFolder("INBOX");
  folder.open(Folder.READ_ONLY);

  // Get directory
  Message message[] = folder.getMessages();
  for (int i=0, n=message.length; i<n; i++) {
     System.out.println(i + ": " + message[i].getFrom()[0]
          + "/t" + message[i].getSubject());
          }

          // Close connection
          folder.close(false);
          store.close();

 对每条消息做些什么由您决定。上面的代码块只是显示这些消息的发件人和主题。技术上讲

,from 地址列表可能为空,而 getFrom()[0] 调用会抛出一个异常。

 要显示全部信息,您可以在用户看完 from 和 subject 域之后给出提示,如用户有需要,就调

用消息的 writeTo() 方法来实现。

          BufferedReader reader = new BufferedReader (
            new InputStreamReader(System.in));

          // Get directory
          Message message[] = folder.getMessages();
          for (int i=0, n=message.length; i<n; i++) {
            System.out.println(i + ": " + message[i].getFrom()[0]
              + "/t" + message[i].getSubject());

            System.out.println("Do you want to read message? " +
              "[YES to read/QUIT to end]");
            String line = reader.readLine();
            if ("YES".equals(line)) {
              message[i].writeTo(System.out);
            } else if ("QUIT".equals(line)) {
              break;
            }
          }

消息和标识的删除:

       消息的删除涉及使用与消息相关的 Flags(标志)。不同 flag 对应不同的状态,有些由

系统定义而有些则由用户定义。下面列出在内部类 Flags.Flag 中预定义的标志:

    * Flags.Flag.ANSWERED
    * Flags.Flag.DELETED
    * Flags.Flag.DRAFT
    * Flags.Flag.FLAGGED
    * Flags.Flag.RECENT
    * Flags.Flag.SEEN
    * Flags.Flag.USER

仅仅因为存在一个标志,并不意味着所有邮件服务器或供应商都支持这个标志。例如,除了删

除消息标志外,POP 协议不再支持其它任何标志。检查是否存在新邮件,这不是个 POP 任务

,而是内建于邮件客户机的任务。为找出哪些标志能被支持,可以用 getPermanentFlags() 向

folder 提出要求。

要删除消息,您可以设置消息的 DELETED flag:

message.setFlag(Flags.Flag.DELETED, true);

首先,请以 READ_WRITE 模式打开 folder:

folder.open(Folder.READ_WRITE);

然后,当所有消息的处理完成后,关闭 folder,并传递一个 true 值,从而擦除(expunge)有

delete 标志的消息。

folder.close(true);

一个 Folder 的 expunge() 方法可以用来删除消息。但 Sun 的 POP3 供应商不支持。其它供应

商有的或许能够实现这一功能,而有的则不能。IMAP 供应商极有可能实现此功能。因为 POP

只支持单个对邮箱的访问,对 Sun 的供应商来说,您必需关闭 folder 以删除消息。

要取消标志,只要传递 false 给 setFlag() 方法就行了。想知道是否设置过标志,可以用 isSet

() 检查。

亲自认证:
       您已经知道 — 如果需要可以用一个 Authenticator 提示用户输入用户名和密码,而不是

将用户名和密码作为字符串传递。在这里您会明确了解怎样更充分的使用认证。

不用主机、用户名和密码与 Store 相连接,而是设置 Properties 来拥有主机,然后告诉

Session 自定义的 Authenticator 实例,如下所示:

// Setup properties
Properties props = System.getProperties();
props.put("mail.pop3.host", host);
 

// Setup authentication, get session
Authenticator auth = new PopupAuthenticator();
Session session = Session.getDefaultInstance(props, auth);
 
// Get the store
Store store = session.getStore("pop3");
store.connect();

 然后,您创建一个 Authenticator 子类并从 getPasswordAuthentication() 方法中返回

PasswordAuthentication 对象。下面就是这样一种实现,其中用户名和密码仅占用一个域。(

这不是一个 Swing 工程教程;只要将两部分输入同一个域,用逗号分隔就行。)

import javax.mail.*;
import javax.swing.*;
import java.util.*;

public class PopupAuthenticator extends Authenticator {
  public PasswordAuthentication getPasswordAuthentication() {
    String username, password;
    String result = JOptionPane.showInputDialog(
      "Enter 'username,password'");
    StringTokenizer st = new StringTokenizer(result, ",");
    username = st.nextToken();
    password = st.nextToken();
    return new PasswordAuthentication(username, password);
  }
}


因为 PopupAuthenticator 涉及到 Swing,它会启动 AWT 的事件处理线程。这一点基本上要求

您在代码中添加一个对 System.exit() 的调用来终止程序。

消息的回复:
       Message 类引入一个 reply() 方法来配置一个新 Message,包括正确的 recipient(收件

人)和添加“Re”(如果没有就添加)的正确的 subject。这样做并没有为消息添加新内容,仅

仅将 from 或 reply-to(被回复人) 头复制给新的收件人。这种方法用一个 boolean 参数指定

消息只回复给发件人(false)或回复给全体(true)。

MimeMessage reply = (MimeMessage)message.reply(false);
reply.setFrom(new InternetAddress("president@whitehouse.gov"));
reply.setText("Thanks");
Transport.send(reply);

在发送消息时要配置 reply to(被回复人) 地址,可以用 setReplyTo() 方法。

消息的转发:
转发消息有一点棘手。没有单独的方法可以调用,您通过对组成消息各部分的处理来组织要转

发的消息。

一条邮件消息可以由多个部分组成。在处理 MIME 消息时,消息中每部分都是 BodyPart,再

特殊些,是 MimeBodyPart。不同的 body part(信体部件或正文部件)结合成一个容器,名

为 Multipart,再特殊些,就是 MimeMultipart。要转发一条消息,您为自己的消息正文创建一

个部件,要转发的消息作为另一部件。并且将两个部件结合成一个 multipart(多部件)。然

后您将这个 multipart 添加到一则已写好恰当地址的消息中,并发送。

本质上就是如此。要将一条消息内容复制到另一条,只要复制 DataHandler (JavaBeans

Activation Framework 中的类)就行了。

// Create the message to forward
Message forward = new MimeMessage(session);

// Fill in header
forward.setSubject("Fwd: " + message.getSubject());
forward.setFrom(new InternetAddress(from));
forward.addRecipient(Message.RecipientType.TO,
  new InternetAddress(to));
 
// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setText(
  "Here you go with the original message:/n/n");

// Create a multi-part to combine the parts
Multipart multipart = new MimeMultipart();
multipart.addBodyPart(messageBodyPart);

// Create and fill part for the forwarded content
messageBodyPart = new MimeBodyPart();
messageBodyPart.setDataHandler(message.getDataHandler());

// Add part to multi part
multipart.addBodyPart(messageBodyPart);

// Associate multi-part with message
forward.setContent(multipart);

// Send message
Transport.send(forward);

附件的处理:
附件是邮件消息的相关资源,如通常不包含在消息正文里文本文件、电子表格或图像等。常见

的邮件程序,如 Eudora 和 pine 之类,可以用 JavaMail API 将资源 attach(附加) 到您的消

息上,就可以在收到消息时得到。
 

附件的发送:
发送附件非常像转发消息。您建立各部分以组成完整消息。完成第一部件,即消息正文后,您

添加其它部件,其中每个 DataHandler 都代表附件,而不是转发消息情况下的共享处理程序。

如果从文件中读附件,附件的数据源是 FileDataSource。而如果从 URL 中读时,附件的数据

源是 URLDataSource。一旦存在 DataSource,只要先把它传递给 DataHandler 构造器,最后

再用 setDataHandler() 把它附加到 BodyPart。假定您要保留附件的原始文件名,最终要做的

是用 BodyPart 的 setFileName() 方法设置与附件相关的文件名。如下所示:

  // Define message
  Message message = new MimeMessage(session);
  message.setFrom(new InternetAddress(from));
  message.addRecipient(Message.RecipientType.TO,
    new InternetAddress(to));
  message.setSubject("Hello JavaMail Attachment");

  // Create the message part
  BodyPart messageBodyPart = new MimeBodyPart();

  // Fill the message
  messageBodyPart.setText("Pardon Ideas");
  Multipart multipart = new MimeMultipart();
  multipart.addBodyPart(messageBodyPart);

  // Part two is attachment
  messageBodyPart = new MimeBodyPart();
  DataSource source = new FileDataSource(filename);
  messageBodyPart.setDataHandler(new DataHandler(source));
  messageBodyPart.setFileName(filename);
  multipart.addBodyPart(messageBodyPart);

  // Put parts in message
  message.setContent(multipart);

  // Send the message
  Transport.send(message);

就消息引入附件时,若程序是个 servlet (小服务程序),除告知消息发送到何处外,还必需

上载附件。可以将 multipart/form-data 表单编码类型(form encoding type)用于每个上载文

件的处理。

<FORM ENCTYPE="multipart/form-data"
    method=post action="/myservlet">
  <INPUT TYPE="file" NAME="thefile">
  <INPUT TYPE="submit" VALUE="Upload">
</FORM>
注意:消息大小由 SMTP 服务器而不是 JavaMail API 来限制。如果您碰到问题,可以考虑用

设置 ms 和 mx 参数的方法增大 Java 堆大小。

附件的获取:

从消息中获取附件比发送它们棘手些,因为 MIME 没有简单的关于附件的概念。当消息包含附

件时,消息的内容是个 Multipart 对象。接着,您需要处理每个 Part,获取主要内容和附件。

标有从 part.getDisposition() 获得的 Part.ATTACHMENT 配置(disposition)的部件(Part)无

疑就是附件。但是,没有配置(以及一个非文本 MIME 类型)和带 Part.INLINE 配置的部件也

可能是附件。当配置要么是 Part.ATTACHMENT,要么是 Part.INLINE 时,这个消息部件的内

容就能被保存。只要用 getFileName() 和 getInputStream() 就能分别得到原始文件名和输入

流。

Multipart mp = (Multipart)message.getContent();
for (int i=0, n=multipart.getCount(); i<n; i++) {
  Part part = multipart.getBodyPart(i));
  String disposition = part.getDisposition();
  if ((disposition != null) &&
       ((disposition.equals(Part.ATTACHMENT) ||
          (disposition.equals(Part.INLINE))) {
    saveFile(part.getFileName(), part.getInputStream());
  }
}

 saveFile() 方法仅依据文件名创建了一个 File,它从输入流中将字节读出,然后写入到文件中

。万一文件已经存在,就在文件名后添加一个数字作为新文件名,如果这个文件名仍存在,则

继续添,直到找不到这样的文件名为止。

// from saveFile()
File file = new File(filename);
for (int i=0; file.exists(); i++) {
  file = new File(filename+i);
}

 上面的代码涵盖了最简单的情况 — 消息中各部件恰当的标记了。要涵盖所有情况,还要在配

置为空时进行处理,并且获取部件的 MIME 类型来进行相应处理。

if (disposition == null) {
  // Check if plain
  MimeBodyPart mbp = (MimeBodyPart)part;
  if (mbp.isMimeType("text/plain")) {
     // Handle plain
  } else {
     // Special non-attachment cases here of image/gif, text/html, ...
  }
  ...
}
 

对 HTML 消息的处理
发送基于 HTML 文件格式消息的工作量比发送纯文本消息多,虽然不一定非要这些多余的工作

量。如何选择完全取决于给定的请求。

HTML 消息的发送:
若您所要做的全部事情是发送一份 HTML 文件的等价物作为消息,但让邮件阅读者为不能提取

任何内嵌图像或相关片段而担心的话,可以使用 Message 的 setContent() 方法,把内容当作

一个 String 传入,并将内容类型设置成 text/html。
 

String htmlText = "<H1>Hello</H1>" +
  "<img src=/"http://www.jguru.com/images/logo.gif/">";
message.setContent(htmlText, "text/html"));

在接收端,如果您用 JavaMail API 提取消息,API 中没有内建的显示 HTML 消息的东西。

JavaMail API 只把它看成一串字节流。要显示 HTML 文件格式的消息,您必需使用 Swing

JEditorPane 或其它第三方 HTML 格式查看器组件。
 

if (message.getContentType().equals("text/html")) {
    String content = (String)message.getContent();
    JFrame frame = new JFrame();
    JEditorPane text = new JEditorPane("text/html", content);
    text.setEditable(false);
    JScrollPane pane = new JScrollPane(text);
    frame.getContentPane().add(pane);
    frame.setSize(300, 300);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.show();
}

在消息中引入图像:
另一方面,如果您想让 HTML 文件格式内容的消息完整(内嵌的图像作为消息的一部分),您

必需把图像作为附件,并且用一个给定的 cid URL 引用图像,其中 cid 是图像附件 Content-ID

头的引用。

嵌入图像的过程与附加文件到消息的过程非常相似,唯一的区别在于您必需通过设置

MimeMultipart 构造器中的子类型(或者说用 setSubType())告知 MimeMultipart 各个相关部

件,并且将这个图像的 Content-ID 头设置成随机字符串,作为图像的 src 在 img 标记中使用

。完整的演示如下。

String file = ...;
// Create the message
Message message = new MimeMessage(session);

// Fill its headers
message.setSubject("Embedded Image");
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO,
  new InternetAddress(to));

// Create your new message part
BodyPart messageBodyPart = new MimeBodyPart();
String htmlText = "<H1>Hello</H1>" +
  "<img src=/"cid:memememe/">";
messageBodyPart.setContent(htmlText, "text/html");

// Create a related multi-part to combine the parts
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(messageBodyPart);

// Create part for the image
messageBodyPart = new MimeBodyPart();

// Fetch the image and associate to part
DataSource fds = new FileDataSource(file);
messageBodyPart.setDataHandler(new DataHandler(fds));
messageBodyPart.setHeader("Content-ID","memememe");

// Add part to multi-part
multipart.addBodyPart(messageBodyPart);
// Associate multi-part with message
message.setContent(multipart);

 

三、JavaMail实例


利用javaMail创建一个简单邮件

代码如下:
public static void main(String[] args) throws Exception{
//1、创建邮件
//设置属性
        Properties props = new Properties();
        //创建session
        Session session = Session.getInstance(props);

        //创建message邮件
        MimeMessage message = new MimeMessage(session);

//2、设置邮件属性
//邮件来自哪
        message.setFrom(new InternetAddress("aa@tongfang.com"));

//邮件发到哪
        message.setRecipient(Message.RecipientType.TO, new InternetAddress

("bb@tongfang.com"));

//邮件的主题
        message.setSubject("Hi");

//3、正文
        MimeBodyPart part = new MimeBodyPart();
        part.setContent("你知道我是谁不?", "text/html;charset=gb2312");

//4、描述关系
        MimeMultipart mm = new MimeMultipart();
        mm.addBodyPart(part);

        //设置
        message.setContent(mm);
        message.saveChanges();
        message.writeTo(new FileOutputStream("D://1.eml"));

    }

利用JavaMail创建一个包含图片的邮件

代码如下:
public class MimeImageMail {
    public static void main(String[] args)throws Exception {

    //1、创建邮件
//MimeMessage message = new MimeMessage(Session.getInstance(new Properties()));

       //设置属性
       Properties props=new Properties();

       //创建session对象
       Session session=Session.getInstance(props);

       //创建邮件对象
       MimeMessage message=new MimeMessage(session);

    //2、设置邮件属性
       //邮件来自哪message.setFrom(new InternetAddress(""));
       message.setFrom(new InternetAddress("aa@tongfang.com"));

       //邮件发去哪message.setRecipient(Message.RecipientType.TO, new InternetAddress

(""));
       message.setRecipient(Message.RecipientType.TO, new InternetAddress

("bbi@tongfang.com"));

       //主题message.setSubject("");
       message.setSubject("看看这是谁啊!");

    //3、创建正文MimeBodyPart text=new MimeBodyPart();
       MimeBodyPart text = new MimeBodyPart();

       text.setContent("呵呵,这小妞长的真俊啊,谁家的啊!<br/><br/><img

src='cid:xx.jpg'>是不错吧", "text/html;charset=utf-8");

    //4、创建一个图片MimeBodyPart image=new MimeBodyPart();
       MimeBodyPart image = new MimeBodyPart();
       image.setDataHandler(new DataHandler(new FileDataSource("src//f.jpg")));
       image.setContentID("xx.jpg");

       //创建描述关系
       MimeMultipart mm = new MimeMultipart();
       mm.addBodyPart(text);
       mm.addBodyPart(image);

       //设置关系
       mm.setSubType("related");//表示消息体中的内容是关联(依赖)组合类型,表示消息体

中的某些内容,依赖另外的资源。
       message.setContent(mm);
       message.saveChanges();
       message.writeTo(new FileOutputStream("D://2.eml"));
    }
}

利用JavaMail创建一个包含附件的邮件

代码如下:
public class AttchMime {
    public static void main(String[] args)throws Exception {

    //1、创建邮件
       MimeMessage message = new MimeMessage(Session.getInstance(new Properties()));

    //2、设置邮件属性
       message.setFrom(new InternetAddress("aa@tongfang.com"));
       message.setRecipient(Message.RecipientType.TO, new InternetAddress

("bb@tongfang.com"));
       message.setSubject("听听这首音乐");

    //3、正文
       MimeBodyPart text= new MimeBodyPart();

       //文本的设置
       text.setContent("我这有一首很好听的音乐,一起分享一下吧<br/><br/><br/>如果感觉

不错就回复一下吧", "text/html;charset=utf-8");

        //附件
       MimeBodyPart attch= new MimeBodyPart();

       //附件的设置
   attch.setDataHandler(new DataHandler(new FileDataSource("src//1.mp3")));
       attch.setFileName("1.mp3");

    //4、描述关系
        MimeMultipart mm = new MimeMultipart();
        mm.addBodyPart(text);
        mm.addBodyPart(attch);

       //设置关系
        mm.setSubType("mixed");//表示消息体中的内容是混合组合类型,可以是文本,声音和

附件等不同邮件内容的混合体。
        message.setContent(mm);
        message.saveChanges();
        message.writeTo(new FileOutputStream("d://3.eml"));
    }
}

利用javaMail创建一个包含两张图片的邮件

代码如下:
public class MimeImage {
    public static void main(String[] args)throws Exception {

       //创建邮件
       MimeMessage message=new MimeMessage(Session.getInstance(new Properties()));

       //设置属性
       message.setFrom(new InternetAddress("aa@tongfang.com"));
       message.setRecipients(Message.RecipientType.TO, "bb@tongfang.com");
       message.setSubject("好多图片啊");

       //设置正文
       MimeBodyPart text=new MimeBodyPart();
       text.setContent("这有好多图片<br/><img src='cid:xx.jpg'><br/><img

src='cid:yy.jpg'><br/>选一张吧", "text/html;charset=utf-8");

       //设置图片
       MimeBodyPart image=new MimeBodyPart();
       image.setDataHandler(new DataHandler(new FileDataSource("src//f.jpg")));
       image.setContentID("xx.jpg");
       MimeBodyPart image1=new MimeBodyPart();
       image1.setDataHandler(new DataHandler(new FileDataSource("src//x.jpg")));
       image1.setContentID("yy.jpg");      

        //描述关系
       //图片与正文的描述关系
       MimeMultipart mm = new MimeMultipart();
       mm.addBodyPart(text);
       mm.addBodyPart(image);
        mm.addBodyPart(image1);  
        mm.setSubType("related");
        message.setContent(mm);
       message.saveChanges();
       message.writeTo(new FileOutputStream("d://4.eml"));
    }
}

利用JavaMail创建一个既包含图片又包含附件的邮件

代码如下:
public class MimeImageAttch {
    public static void main(String[] args)throws Exception {

       //1、创建邮件
       MimeMessage message=new MimeMessage(Session.getInstance(new Properties()));

       //2、设置属性
       message.setFrom(new InternetAddress("aa@tongfang.com"));
       message.setRecipients(Message.RecipientType.TO, "bb@tongfang.com");
       message.setSubject("既有图片又有附件");

       //3、设置正文
       MimeBodyPart text=new MimeBodyPart();
       text.setContent("这有好多图片<br/><img src='cid:xx.jpg'><br/><img

src='cid:yy.jpg'><br/>选一张吧", "text/html;charset=utf-8");

       //4、设置图片
       MimeBodyPart image=new MimeBodyPart();
       image.setDataHandler(new DataHandler(new FileDataSource("src//f.jpg")));
       image.setContentID("xx.jpg");
       MimeBodyPart image1=new MimeBodyPart();
       image1.setDataHandler(new DataHandler(new FileDataSource("src//x.jpg")));
       image1.setContentID("yy.jpg");
    

       //5、设置附件
       MimeBodyPart attch = new MimeBodyPart();
       DataHandler dh1 = new DataHandler(new FileDataSource("src//大笑.mp3"));
       attch.setDataHandler(dh1);
       String filename = dh1.getName();
       attch.setFileName(MimeUtility.encodeText(filename));

        //6、描述关系

       //图片与正文的描述关系
       MimeMultipart mm = new MimeMultipart();
       mm.addBodyPart(text);
       mm.addBodyPart(image);
       mm.addBodyPart(image1);
       mm.setSubType("related");      

       //图片与正文的 bodypart
       MimeBodyPart part = new MimeBodyPart();
       part.setContent(mm);

      
       //附件与以上的bodypart关系
       MimeMultipart m = new MimeMultipart();
       m.addBodyPart(part);
       m.addBodyPart(attch);
       m.setSubType("mixed");      

       //把MIME消息设置到message中
       message.setContent(m);
       message.saveChanges();
       message.writeTo(new FileOutputStream("d://5.eml"));
       }
}

 
//附件

       MimeBodyPart attch= new MimeBodyPart();
       //附件的设置
attch.setDataHandler(new DataHandler(new FileDataSource("src//1.mp3")));
       attch.setFileName("1.mp3");

如果上边代码中的附件标题是中文的就会出现乱码问题,下面的代码就解决了乱码问题

//附件
       MimeBodyPart attch = new MimeBodyPart();
       DataHandler dh1 = new DataHandler(new FileDataSource("src//大笑.mp3"));
       attch.setDataHandler(dh1);
       String filename = dh1.getName();
       attch.setFileName(MimeUtility.encodeText(filename));


 

四、JavaMail发送HTML格式内容

 

例子1
package com.test; 
 
import java.io.IOException; 
import java.util.Date; 
import java.util.Properties; 
 
import javax.activation.DataHandler; 
import javax.mail.Address; 
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.InternetAddress; 
import javax.mail.internet.MimeMessage; 
import javax.mail.util.ByteArrayDataSource; 
 
 
public class EmailTest { 
 
    String serverauth ="true"; 
    private String mailhost=null;//SMTP邮件接收服务器  
    private String outgoingport=null;//SMTP邮件接收服务器端口号,默认为25号端口  
    private String senderadress=null;//邮件发送者的邮箱地址  
    private String senderpassword=null;//邮件发送者的邮箱密码  
    
    /**
    * Default constructor
    */ 
    public EmailTest(String mailserver,String mailaddress,String password) { 
        mailhost=mailserver; 
        senderadress=mailaddress; 
        senderpassword=password; 
    } 
    
    public boolean sendMailWithAtt(String email_to,String email_subject, String email_body)

throws IOException { 
        boolean result = true; 
        try { 
            //set properties  
            Properties props = new Properties(); 
            props.setProperty("mail.transport.protocol", "smtp"); 
            //发送邮件服务器  
            props.setProperty("mail.host", mailhost); 
            if (serverauth != null&& serverauth.equalsIgnoreCase("true")) { 
                props.put("mail.smtp.auth", serverauth); 
            } 
            if (outgoingport!= null&& !outgoingport.trim().equals("")) { 
                props.put("mail.smtp.port", outgoingport); 
                props.put("mail.smtp.socketFactory.port",outgoingport); 
                props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory"); 
                props.put("mail.smtp.socketFactory.fallback", "false"); 
                props.setProperty("mail.smtp.quitwait", "false"); 
            } 
            // Get Session using Properties Object  
            Session session = null; 
            if (serverauth != null&& serverauth.equalsIgnoreCase("true")) { 
                Authenticator authen = new EmailAuthenticator(); 
                session = Session.getInstance(props, authen); 
            } else { 
                session = Session.getInstance(props); 
            } 
            // Define message  
            MimeMessage message = new MimeMessage(session); 
            message.setFrom(new InternetAddress(senderadress)); 
            /*
            * Multiple(多个接收者)
            */ 
            if ( email_to != null && !email_to.trim().equals( "" )) { 
                String tos[] = email_to.trim().split(","); 
                Address toAdd[] = new Address[tos.length]; 
                for ( int i = 0; i < toAdd.length; i++ ) { 
                    toAdd[i] = new InternetAddress( tos[i] ); 
                } 
                message.addRecipients(Message.RecipientType.TO, toAdd);        
            }            
            message.setSubject(email_subject); 
            message.setSentDate(new Date()); 
            StringBuffer sb = new StringBuffer(); 
            sb.append("<HTML>/n"); 
            sb.append("<HEAD>/n"); 
            sb.append("<TITLE>/n"); 
            sb.append(email_subject + "/n"); 
            sb.append("</TITLE>/n"); 
            sb.append("</HEAD>/n"); 
 
            sb.append("<BODY>/n"); 
            sb.append("<H1>" + email_subject + "</H1>" + "/n"); 
 
            sb.append(email_body+"/n"); 
 
            sb.append("</BODY>/n"); 
            sb.append("</HTML>/n"); 
            message.setDataHandler(new DataHandler( 
                    new ByteArrayDataSource(sb.toString(), "text/html"))); 
 
            Transport.send(message); 
        } catch (MessagingException mex) { 
            result = false; 
            mex.printStackTrace(); 
        } 
        return result; 
    } 
 
    /**
    * Inner class
    */ 
    private class EmailAuthenticator extends javax.mail.Authenticator { 
        public PasswordAuthentication getPasswordAuthentication() { 
            return new PasswordAuthentication(senderadress, senderpassword); 
        } 
    } 
    
    /**
    * 发送邮件的测试方法
     * @throws IOException 
    */ 
    public static void main(String[] args) throws IOException { 
        EmailTest jm=new EmailTest("smtp.qq.com","wangking717@qq.com", "xxxxx"); 
        boolean sendresult=jm.sendMailWithAtt("wangking717@qq.com", "正在测试邮件发送",

"sss<img src="http://www.ceconlinebbs.com/images/CEC-Forum-logo.gif"

mce_src="http://www.ceconlinebbs.com/images/CEC-Forum-logo.gif"

alt=/"555555/"/><br/><a href="/" mce_href="/""http://www.baidu.com/">我的链接

</a><br/><font color=/"red/"><b>哈哈</b></font>");  
        if(sendresult){ 
            System.out.println("邮件发送成功"); 
        }else{ 
            System.out.println("邮件发送失败"); 
        } 
    } 


例子2
 通常情况下,发送html格式的邮件,需要手工创建一个嵌入了HTML标签的字符串,然后将这

个字符串设为Content进行发送。

但是如果邮件内容比较多的话,构造这样一个字符串将变得非常复杂。经过实践,我总结了下

面的方法来发送嵌入大量HTML格式的邮件。

首先,将需要通过邮件发送的内容,写入一个html文件模板中。然后在程序中将此文件读入一

个字符串中,可以使用StringBuffer来构造。

最后,将构造好的String字符串设置到邮件的Content中进行发送,收到的邮件中就可以显示此

HTML模板的内容。需要注意的是,html模板文件

和setContent(String, String)方法中的字符编码格式必须保持一致,否则收到的邮件会乱码。

再者,将需要发送的内容写入html模板文件的好处在于,模板中可以使用CSS定义来调整显示

效果。同时也为程序动态创建模板文件内容提供了便利。
 

不过,有一个缺点就是,这种方式需要将整个邮件模板html文件读入内存,这样对内存的占用

比较大,值得注意。

示例代码如下:
//从html模板中读取邮件内容
public String readHTML(String spath) {
    InputStreamReader isReader = null;
    BufferedReader bufReader = null;
    StringBuffer buf = new StringBuffer();
    try {
         File file = new File(spath);
         isReader = new InputStreamReader(new FileInputStream(file), "utf-8");
         bufReader = new BufferedReader(isReader, 1);
         String data;
         while((data == bufReader.readLine())  != null) {
                buf.append(data);
         }

    } catch (Exception e) {
    //TODO 处理异常
    } finally {
      //TODO 关闭流
       isReader.close();
      bufReader.close();
    }

    return buf.toString();

}

//发送邮件
public void sendHTML() {
    BodyPart html = new MimeBodyPart();
   //生成的模板文件,注意此html模板文件的字符编码,请使用和红色部分一致的编码,否则

收到的邮件会乱码
   String mailTemplete = "./mailTemplete.html";
    String readHTML = readHTML(mailTemplete);
   html.setContent(readHTML, "text/html; charset=utf-8");
}

注意两处编码一致

发送HTML格式邮件

一、开发思想:
1、发送HTML的公共主程序可以参考java的发送html邮件的程序;
2、在邮件中显示出html的样式效果是根据EBS中HTML报表的代码样式转换而来


二、实现程序:
1、主程序SendHtmlMail.java中的host、user、pwd、from这里是写死了,可以提取出来当参数传入更具有通用型
package cux.oracle.apps.pos.Util;

import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.util.Date;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import oracle.apps.fnd.cp.request.JavaConcurrentProgram;

/**
* 创建 HTML 格式的邮件
*
* @author Jason Gu
*/
public class SendHtmlMail
{

  public String sendMessage(String host, String user, String pwd, String from,
                            String to, String subject,
                            String body) throws MessagingException,
                                                java.io.UnsupportedEncodingException
  {
    Properties props = new Properties();

    // 设置发送邮件的邮件服务器的属性
    props.put("mail.smtp.host", host);

    // 需要经过授权,也就是用户名和密码的校验,这样才能通过验证(一定要有这一条)
    props.put("mail.smtp.auth", "true");

    // 创建该邮件应用程序所需的环境信息以及会话信息
    Session session = Session.getDefaultInstance(props);

    // 有了这句便可以在发送邮件的过程中在console处显示过程信息,供调试使
    // 用(你可以在控制台(console)上看到发送邮件的过程)
    session.setDebug(true);

    // 根据上面的 Session 实例创建 MimeMessage 实例,即一封邮件
    MimeMessage msg = new MimeMessage(session);

    try
    {
      // 设置发件人地址
      msg.setFrom(new InternetAddress(from));

      // 设置收件人地址
      msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));

      // 设置 E-mail 主题
      msg.setSubject(subject);

      // 设置发送时间
      msg.setSentDate(new Date());

      // 设置 E-mail 正文部分
      // msg.setText(body);
      msg.setContent(body, "text/html;charset = UTF-8");

      // 保存对该 MimeMessage 实例的更改
      msg.saveChanges();

      // 发送邮件
      Transport transport = session.getTransport("smtp");

      // 连接服务器的邮箱
      transport.connect(host, user, pwd);

      // 把邮件发送出去
      transport.sendMessage(msg, msg.getAllRecipients());
      transport.close();
      // 将 msg 对象中内容写入文件
      msg.writeTo(new FileOutputStream("SendHtmlMail.eml"));
      return "S";
    } catch (Exception e)
    {
      e.printStackTrace();
      return "E";
    }
  }

  public static String main(String to,String subject,String body) throws MessagingException,
                                              UnsupportedEncodingException
  {
    String host = "172.17.27.249"; // smtp服务器
    String user = "Ebs-admin"; // 用户名
    String pwd = "[CONTRACT]2013(approval)"; // 密码
    String from = "Ebs-admin@jd.com";

    SendHtmlMail sendmail = new SendHtmlMail();
    String result =
      sendmail.sendMessage(host, user, pwd, from, to, subject, body);
    return result;
  }
}

2、邮件内容和收件人的程序CuxPtebsReport.java:
package cux.oracle.apps.pos.Util;

import java.sql.PreparedStatement;

import java.util.Date;

import java.text.SimpleDateFormat;

import oracle.apps.fnd.common.VersionInfo;
import oracle.apps.fnd.cp.request.CpContext;
import oracle.apps.fnd.cp.request.JavaConcurrentProgram;
import oracle.apps.fnd.cp.request.LogFile;
import oracle.apps.fnd.cp.request.ReqCompletion;
import oracle.apps.fnd.framework.OAException;
import oracle.apps.fnd.util.ParameterList;

import java.sql.Connection;
import java.sql.ResultSet;

import oracle.apps.fnd.util.NameValueType;

import cux.oracle.apps.pos.Util.SendHtmlMail;

/**
*  请求调用生成 HTML 格式的邮件
*
* @author Jason Gu
*/
public class CuxPtebsReport implements JavaConcurrentProgram
{
  public static final String RCS_ID =
    "$Header: CuxRepayReport.java 120.1 2013/09/06 14:36:06 sabatra noship $";
  public static final boolean RCS_ID_RECORDED =
    VersionInfo.recordClassVersion("$Header: CuxRepayReport.java 120.1 2013/09/06 14:36:06 sabatra noship $",
                                   "%packagename%");
  protected LogFile log;
  protected ReqCompletion reqc;
  private static SimpleDateFormat mDateFormat;
  private String l_return_status = "S";

  public void runProgram(CpContext cpContext)
  {
    ParameterList lPara = cpContext.getParameterList();
    StringBuffer com_content = new StringBuffer();
    StringBuffer dept_content = new StringBuffer();
    String sendresult = "S";
    String cux_combody = new String();
    String cux_deptbody = new String();
    String body = new String();
    try
    {
      this.log = cpContext.getLogFile();
      this.reqc = cpContext.getReqCompletion();

      Connection con = cpContext.getJDBCConnection();

      this.log.writeln("input parameters list:", 1);
      while (lPara.hasMoreElements())
      {
        NameValueType nvt = lPara.nextParameter();
        this.log.writeln(nvt.getName() + ":" + nvt.getValue(), 1);

      }
      //获取当前日期
      java.util.Calendar c = java.util.Calendar.getInstance();
      java.text.SimpleDateFormat f =
        new java.text.SimpleDateFormat("yyyy年MM月dd日");
      //邮件主题
      String subject = f.format(c.getTime()) + "新增公司和部门";
      //表格标题
      String com = f.format(c.getTime()) + "数据新增公司";
      String dept = f.format(c.getTime()) + "数据新增部门";
      String comheader =
        "<table border=1 cellspacing=0 cellpadding=2><tr><td colspan=4 align=center>" +
        com +
        "</td></tr><tr><td nowrap>公司编码</td><td nowrap>公司名称</td><td nowrap>生效日期</td><td>最后更新日期</td></tr>";
      String combody =
        "<tr align=left><td nowrap>VAR_A</td><td nowrap>VAR_B</td><td nowrap>VAR_C</td><td nowrap>VAR_D</td></tr>";
      String deptheader =
        "<table border=1 cellspacing=0 cellpadding=2><tr><td colspan=4 align=center>" +
        dept +
        "</td></tr><tr><td nowrap>部门编码</td><td nowrap>部门名称</td><td nowrap>生效日期</td><td>最后更新日期</td></tr>";
      String deptbody =
        "<tr align=left><td nowrap>VAR_E</td><td nowrap>VAR_F</td><td nowrap>VAR_G</td><td nowrap>VAR_H</td></tr>";
      String foot = "</table><br><br>";

      PreparedStatement compre = null;
      PreparedStatement deptpre = null;
      PreparedStatement updatecompre = null;
      PreparedStatement updatedeptpre = null;
      PreparedStatement mailtopre = null;
      ResultSet comresult = null;
      ResultSet deptresult = null;
      ResultSet mailtoresult = null;
      ResultSet updatecomresult = null;
      ResultSet updatedeptresult = null;
      this.log.writeln("start datebase connection...", 1);

      //新增公司数据
      String comsql =
        "SELECT company_code, company_name, to_char(start_date, 'yyyy-mm-dd') start_date, to_char(last_update_date,'yyyy-mm-dd') last_update_date \n" +
        "FROM cux_test_com_data WHERE send_flag = 'N' \n";

      compre = con.prepareStatement(comsql);
      comresult = compre.executeQuery();
      while (comresult.next())
      {
        this.log.writeln("com_code:" + comresult.getString("company_code"), 1);
        cux_combody = combody;
        cux_combody =
            cux_combody.replace("VAR_A", comresult.getString("company_code"));
        cux_combody =
            cux_combody.replace("VAR_B", comresult.getString("company_name"));
        String com_start_date = comresult.getString("start_date");
        if (com_start_date == null)
        {
          com_start_date = " ";
        }
        String date = com_start_date;
        cux_combody = cux_combody.replace("VAR_C", date);
        cux_combody =
            cux_combody.replace("VAR_D", comresult.getString("last_update_date"));
        com_content.append(cux_combody);
      }

      //部门新增数据
      String deptsql =
        "SELECT dept_code, dept_name, to_char(start_date,'yyyy-mm-dd') start_date,to_char(last_update_date,'yyyy-mm-dd') last_update_date \n" +
        "  FROM cux_test_dept_data where send_flag = 'N'";
      deptpre = con.prepareStatement(deptsql);
      deptresult = deptpre.executeQuery();
      //deptresult.
      while (deptresult.next())
      {
        cux_deptbody = deptbody;
        cux_deptbody =
            cux_deptbody.replace("VAR_E", deptresult.getString("dept_code"));
        cux_deptbody =
            cux_deptbody.replace("VAR_F", deptresult.getString("dept_name"));
        String dept_start_date = deptresult.getString("start_date");
        if (dept_start_date == null)
        {
          dept_start_date = " ";
        }
        String start_date = dept_start_date;
        cux_deptbody = cux_deptbody.replace("VAR_G", start_date);
        cux_deptbody =
            cux_deptbody.replace("VAR_H", deptresult.getString("last_update_date"));
        dept_content.append(cux_deptbody);
      }

      int resp_id = cpContext.getRespId();
      this.log.writeln("resp_id:" + resp_id, 2);
      //获取职责id
      String addrsql =
        "SELECT distinct pf.email_address\n" + "        FROM fnd_user_resp_groups_direct c, fnd_responsibility r, fnd_user fu, per_all_people_f pf\n" +
        "       WHERE c.responsibility_id = r.responsibility_id\n" +
        "         AND c.user_id = fu.user_id\n" +
        "         AND pf.email_address is not null\n" +
        "         AND fu.employee_id = pf.person_id\n" +
        "         AND c.responsibility_id = " + resp_id;
      //this.log.writeln("addrsql:" + addrsql, 2);
      mailtopre = con.prepareStatement(addrsql);
      mailtoresult = mailtopre.executeQuery();
      while (mailtoresult.next())
      {
        String addr = mailtoresult.getString("email_address");
        this.log.writeln("addr:" + addr, 1);
        if (addr != null && addr != "")
        {
          if (!"".equals(com_content.toString()) ||
              !"".equals(dept_content.toString()))
          {
            this.log.writeln("com not null or dept not null in", 4);
            //邮件内容
            if (com_content.toString().equals("") &&
                !"".equals(dept_content.toString()))
            {
              this.log.writeln("com null,dept not null", 4);
              body = deptheader + dept_content.toString() + foot;
            } else if (!"".equals(com_content.toString()) &&
                       dept_content.toString().equals(""))
            {
              this.log.writeln("com not null,dept null", 4);
              body = comheader + com_content.toString() + foot;
            } else if (!"".equals(com_content.toString()) &&
                       !"".equals(dept_content.toString()))
            {
              this.log.writeln("com not null,dept not null", 4);
              body =
                  comheader + com_content.toString() + foot + deptheader + dept_content.toString() +
                  foot;
            }
            //发送邮件
            SendHtmlMail sendmail = new SendHtmlMail();
            sendresult = sendmail.main(addr, subject, body);
          } else
          {
            this.log.writeln("没有需要发送的内容", 4);
          }
        } else
        {
          write("resp_id:" + resp_id + "未维护邮箱");
        }
      }

      if (!"S".equals(sendresult))
      {
        this.l_return_status = "E";
      }

      if ("S".equals(this.l_return_status))
      {
        this.reqc.setCompletion(0, "program completed Successfully.");
        String update_com_sql =
          "UPDATE cux_test_com_data c\n" + "   SET c.send_flag = 'Y', c.last_update_date = SYSDATE, c.last_updated_by = fnd_global.user_id\n" +
          " WHERE c.send_flag = 'N'";
        updatecompre = con.prepareStatement(update_com_sql);
        updatecomresult = updatecompre.executeQuery();
        String update_dept_sql =
          "UPDATE cux_test_dept_data d\n" + "   SET d.send_flag = 'Y', d.last_update_date = SYSDATE, d.last_updated_by = fnd_global.user_id\n" +
          " WHERE d.send_flag = 'N'";
        updatedeptpre = con.prepareStatement(update_dept_sql);
        updatedeptresult = updatedeptpre.executeQuery();
      } else
      {
        this.reqc.setCompletion(2,
                                "program completed with error. Please see request log for details.");
      }
    }

    catch (OAException localOAException)
    {
      localOAException.printStackTrace();
      this.reqc.setCompletion(2, localOAException.getMessage());
    } catch (Exception localException)
    {
      localException.printStackTrace();
      this.reqc.setCompletion(2, localException.toString());
    }
  }

  protected void write(String paramString)
  {
    this.log.writeln(getCurrDateStr() + paramString, 0);
  }

  private String getCurrDateStr()
  {
    if (mDateFormat == null)
    {
      mDateFormat = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss:SSS");
    }

    return "[" + mDateFormat.format(new Date()) + "] ";
  }
}

0 0
原创粉丝点击