javamail_javamailapi - CSDN
  • JavaMail发送和接收邮件

    万次阅读 多人点赞 2009-03-09 10:42:00
    一、JavaMail概述: JavaMail是由Sun定义的一套收发电子邮件的API,不同的厂商可以提供自己的实现类。但它并没有包含在JDK中,而是作为JavaEE的一部分。 厂商所提供的JavaMail服务程序可以有选择地实现某些邮件...

    一、JavaMail概述:

           JavaMail是由Sun定义的一套收发电子邮件的API,不同的厂商可以提供自己的实现类。但它并没有包含在JDK中,而是作为JavaEE的一部分。

           厂商所提供的JavaMail服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括:

    l         SMTP:简单邮件传输协议,用于发送电子邮件的传输协议;

    l         POP3:用于接收电子邮件的标准协议;

    l         IMAP:互联网消息协议,是POP3的替代协议。

    这三种协议都有对应SSL加密传输的协议,分别是SMTPSPOP3SIMAPS

    JavaMail服务提供程序之外,JavaMail还需要JAF(JavaBeans Activation Framework)来处理不是纯文本的邮件内容,这包括MIME(多用途互联网邮件扩展)、URL页面和文件附件等内容。下图描述了JavaMail的体系结构。

     

    mail.jar:此JAR文件包含JavaMail APISun提供的SMTPIMAPPOP3服务提供程序;

    activation.jar:此JAR文件包含JAF APISun的实现。

     

    二、对相关协议的回顾:

           1、介绍

           在研究 JavaMail API 的细则之前,让我们回顾用于 API 的协议。基本上,您会逐渐熟悉并喜爱的协议有四个:

     

        * SMTP

        * POP

        * IMAP

        * MIME

     

    您还将碰到 NNTP 和其它协议。理解所有协议的基本知识将有助于您理解如何使用 JavaMail API。虽然不了解这些协议您照样可以用这个 API,却不能够克服那些基础协议的局限性。如果我们精选的协议不能支持某种性能,JavaMail API 决不能魔术般的将这种性能添加上去。(您很快就会看到,在处理 POP 时这将成为一个难题。)

          

           2SMTP

           简单邮件传输协议(Simple Mail Transfer ProtocolSMTP)由 RFC 821 定义。它定义了发送电子邮件的机制。在 JavaMail API 环境中,您基于 JavaMail 的程序将和您的公司或因特网服务供应商的(Internet Service Provider'sISP'sSMTP 服务器通信。SMTP 服务器会中转消息给接收方 SMTP 服务器以便最终让用户经由 POP IMAP 获得。这不是要求 SMTP 服务器成为开放的中继,尽管 SMTP 服务器支持身份验证,不过还是得确保它的配置正确。像配置服务器来中继消息或添加删除邮件账号这类任务的实现,JavaMail API 中并不支持。

     

           3POP

           POP 代表邮局协议(Post Office Protocol)。目前用的是版本 3,也称 POP3RFC 1939 定义了这个协议。POP 是一种机制,因特网上大多数人用它得到邮件。它规定每个用户一个邮箱的支持。这就是它所能做的,而这也造成了许多混淆。使用 POP 时,用户熟悉的许多性能并不是由 POP 协议支持的,如查看有几封新邮件消息这一性能。这些性能内建于如 Eudora Microsoft Outlook 之类的程序中,它们能记住一些事,诸如最近一次收到的邮件,还能计算出有多少是新的。所以当使用 JavaMail API 时,如果您想要这类信息,您就必须自己算。

     

           4IMAP

           IMAP 是更高级的用于接收消息的协议。在 RFC 2060 中被定义,IMAP 代表因特网消息访问协议(Internet Message Access Protocol),目前用的是版本 4,也称 IMAP4。在用到 IMAP 时,邮件服务器必需支持这个协议。不能仅仅把使用 POP 的程序用于 IMAP,并指望它支持 IMAP 所有性能。假设邮件服务器支持 IMAP,基于 JavaMail 的程序可以利用这种情况 用户在服务器上有多个文件夹(folder),并且这些文件夹可以被多个用户共享。

           因为有这一更高级的性能,您也许会认为所有用户都会使用 IMAP。事实并不是这样。要求服务器接收新消息,在用户请求时发送到用户手中,还要在每个用户的多个文件夹中维护消息。这样虽然能将消息集中备份,但随着用户长期的邮件夹越来越大,到磁盘空间耗尽时,每个用户都会受到损失。使用 POP,就能卸载邮件服务器上保存的消息了。

          

           5MIME

           MIME 代表多用途因特网邮件扩展标准(Multipurpose Internet Mail Extensions)。它不是邮件传输协议。但对传输内容的消息、附件及其它的内容定义了格式。这里有很多不同的有效文档:RFC 822RFC 2045RFC 2046 RFC 2047。作为一个 JavaMail API 的用户,您通常不必对这些格式操心。无论如何,一定存在这些格式而且程序会用到它。

     

           6NNTP及其他

           因为 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连接超时时间,单位为毫秒,默认为永不超时

           其他几个协议也有类似的一系列属性,如POP3mail.pop3.hostmail.pop3.port以及IMAPmail.imap.hostmail.imap.port等。更详细的信息请查看com.sun.mail.smtpcom.sun.mail.pop3com.sun.mail.imap这三个包的Javadochttp://java.sun.com/products/javamail/javadocs/index.html

     

    Session:会话对象

           Session是一个很容易被误解的类,这归咎于混淆视听的类名。千万不要以为这里的SessionHttpSession一样代表真实的交互会话,但创建Session对象时,并没有对应的物理连接,它只不过是一对配置信息的集合。Session的主要作用包括两个方面:

           1)接收各种配置属性信息:通过Properties对象设置的属性信息;

           2)初始化JavaMail环境:根据JavaMail的配置文件,初始化JavaMail环境,以便通过Session对象创建其他重要类的实例。

           所以,如果把Session更名为Configure也许更容易理解一些。JavaMail提供者在Jar包的META-INF目录下,通过以下文件提供了基本配置信息,以便session能够根据这个配置文件加载提供者的实现类:

    l         javamail.providersjavamail.default.providers

    l         javamail.address.mapjavamail.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.jarMETA-INF目录下的javamail.providers

           3)如果2)不存在相应的配置文件,使用类路径下的mail.jarMETA-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,则后续调用通过nullAuthenticator入参或直接使用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实例,未使用安全认证信息;

           SessionJavaMail提供者配置文件以及设置属性信息的“容器”,Session本身不会和邮件服务器进行任何的通信。所以在一般情况下,我们仅需要通过getDefaultInstance()获取一个共享的Session实例就可以了,下面的代码创建了一个Session实例:

           Properties props = System.getProperties();

        props.setProperty("mail.transport.protocol", "smtp");             

        Session session = Session.getDefaultInstance(props);

     

    TransportStore:传输和存储

           邮件操作只有发送或接收两种处理方式,JavaMail将这两种不同操作描述为传输(javax.mail.Transport)和存储(javax.mail.Store),传输对应邮件的发送,而存储对应邮件的接收。

           Session提供了几个用于创建TransportStore实例的方法,在具体讲解这些方法之前,我们事先了解一下Session创建TransportStore的内部机制。我们知道提供者在javamail.providers配置文件中为每一种支持的邮件协议定义了实现类,Session根据协议类型(stmppop3等)和邮件操作方式(传输和存储)这两个信息就可以定位到一个实例类上。比如,指定stmp协议和transport类型后,Session就会使用com.sun.mail.smtp.SMTPTransport实现类创建一个Transport实例,而指定pop3协议和store类型时,则会使用com.sun.mail.pop3.POP3Store实例类创建一个Store实例。Session提供了多个重载的getTransport()getStore()方法,这些方法将根据SessionProperties属性设置情况进行工作,影响这两套方法工作的属性包括:

    属性名

    说明

    mail.transport.protocol

    默认的邮件传输协议,例如,smtp

    mail.store.protocol

    默认的存储邮件协议,例如:pop3

    mail.host

    默认的邮件服务地址,例如:192.168.67.1

    mail.user

    默认的登陆用户名,例如:zapldy

    下面,我们再回头来了解SessiongetTransport()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.MimeMessageMimeMessage 是个能理解 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 exchangeMX)记录,但这些功能不属于 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);

    展开全文
  • JavaWeb-JavaMail邮件开发

    万次阅读 2018-07-18 14:21:00
    Email的历史比Web还要久远,直到现在,Email也是互联网上应用非常广泛的服务。 几乎所有的编程语言都支持发送和接收电子邮件,但是,先等等,在我们开始编写代码之前,有必要搞清楚电子邮件是如何在互联网上...

     

    javamail

    Email的历史比Web还要久远,直到现在,Email也是互联网上应用非常广泛的服务。

    几乎所有的编程语言都支持发送和接收电子邮件,但是,先等等,在我们开始编写代码之前,有必要搞清楚电子邮件是如何在互联网上运作的。

    我们来看看传统邮件是如何运作的。假设你现在在北京,要给一个香港的朋友发一封信,怎么做呢?

    首先你得写好信,装进信封,写上地址,贴上邮票,然后就近找个邮局,把信仍进去。

    信件会从就近的小邮局转运到大邮局,再从大邮局往别的城市发,比如先发到天津,再走海运到达香港,也可能走京九线到香港,但是你不用关心具体路线,你只需要知道一件事,就是信件走得很慢,至少要几天时间。

    信件到达香港的某个邮局,也不会直接送到朋友的家里,因为邮局的叔叔是很聪明的,他怕你的朋友不在家,一趟一趟地白跑,所以,信件会投递到你的朋友的邮箱里,邮箱可能在公寓的一层,或者家门口,直到你的朋友回家的时候检查邮箱,发现信件后,就可以取到邮件了。

    电子邮件的流程基本上也是按上面的方式运作的,只不过速度不是按天算,而是按秒算。

    现在我们回到电子邮件,假设我们自己的电子邮件地址是me@163.com,对方的电子邮件地址是friend@sina.com(注意地址都是虚构的哈),现在我们用Outlook或者Foxmail之类的软件写好邮件,填上对方的Email地址,点“发送”,电子邮件就发出去了。这些电子邮件软件被称为MUA:Mail User Agent——邮件用户代理。

    Email从MUA发出去,不是直接到达对方电脑,而是发到MTA:Mail Transfer Agent——邮件传输代理,就是那些Email服务提供商,比如网易、新浪等等。由于我们自己的电子邮件是163.com,所以,Email首先被投递到网易提供的MTA,再由网易的MTA发到对方服务商,也就是新浪的MTA。这个过程中间可能还会经过别的MTA,但是我们不关心具体路线,我们只关心速度。

    Email到达新浪的MTA后,由于对方使用的是@sina.com的邮箱,因此,新浪的MTA会把Email投递到邮件的最终目的地MDA:Mail Delivery Agent——邮件投递代理。Email到达MDA后,就静静地躺在新浪的某个服务器上,存放在某个文件或特殊的数据库里,我们将这个长期保存邮件的地方称之为电子邮箱。

    同普通邮件类似,Email不会直接到达对方的电脑,因为对方电脑不一定开机,开机也不一定联网。对方要取到邮件,必须通过MUA从MDA上把邮件取到自己的电脑上。

    所以,一封电子邮件的旅程就是:

    发件人 -> MUA -> MTA -> MTA -> 若干个MTA -> MDA <- MUA <- 收件人

    email

    有了上述基本概念,要编写程序来发送和接收邮件,本质上就是:

    1. 编写MUA把邮件发到MTA;
    2. 编写MUA从MDA上收邮件。

    发邮件时,MUA和MTA使用的协议就是SMTP:Simple Mail Transfer Protocol,后面的MTA到另一个MTA也是用SMTP协议。

    收邮件时,MUA和MDA使用的协议有两种:POP:Post Office Protocol,目前版本是3,俗称POP3;IMAP:Internet Message Access Protocol,目前版本是4,优点是不但能取邮件,还可以直接操作MDA上存储的邮件,比如从收件箱移到垃圾箱,等等。

    邮件客户端软件在发邮件时,会让你先配置SMTP服务器,也就是你要发到哪个MTA上。假设你正在使用163的邮箱,你就不能直接发到新浪的MTA上,因为它只服务新浪的用户,所以,你得填163提供的SMTP服务器地址:smtp.163.com,为了证明你是163的用户,SMTP服务器还要求你填写邮箱地址和邮箱口令,这样,MUA才能正常地把Email通过SMTP协议发送到MTA。

    类似的,从MDA收邮件时,MDA服务器也要求验证你的邮箱口令,确保不会有人冒充你收取你的邮件,所以,Outlook之类的邮件客户端会要求你填写POP3或IMAP服务器地址、邮箱地址和口令,这样,MUA才能顺利地通过POP或IMAP协议从MDA取到邮件。

    在收发邮件前,请先准备好至少两个电子邮件,如xxx@163.com,xxx@sina.com,xxx@qq.com等,注意两个邮箱不要用同一家邮件服务商。

    1. 邮件协议

    1.1 收发邮件

    发邮件大家都会吧!发邮件是从客户端把邮件发送到邮件服务器,收邮件是把邮件服务器的邮件下载到客户端。

    javamail

    我们在163、126、QQ、sohu、sina等网站注册的Email账户,其实就是在邮件服务器中注册的。这些网站都有自己的邮件服务器。

    1.2 邮件协议概述

    与HTTP协议相同,收发邮件也是需要有传输协议的。

    • SMTP:(Simple Mail Transfer Protocol,简单邮件传输协议)发邮件协议;
    • POP3:(Post Office Protocol Version 3,邮局协议第3版)收邮件协议;
    • IMAP:(Internet Message Access Protocol,因特网消息访问协议)收发邮件协议,我们的课程不涉及该协议。

    1.3 理解邮件收发过程

    其实你可以把邮件服务器理解为邮局!如果你需要给朋友寄一封信,那么你需要把信放到邮筒中,这样你的信会“自动”到达邮局,邮局会把信邮到另一个省市的邮局中。然后这封信会被送到收信人的邮箱中。最终收信人需要自己经常查看邮箱是否有新的信件。

    其实每个邮件服务器都由SMTP服务器和POP3服务器构成,其中SMTP服务器负责发邮件的请求,而POP3负责收邮件的请求。

    javamail

    当然,有时我们也会使用163的账号,向126的账号发送邮件。这时邮件是发送到126的邮件服务器,而对于163的邮件服务器是不会存储这封邮件的。

    javamail

    1.4 邮件服务器名称

    smtp服务器的端口号为25,服务器名称为smtp.xxx.xxx。 
    pop3服务器的端口号为110,服务器名称为pop3.xxx.xxx。

    例如:

    • 163:smtp.163.com和pop3.163.com;
    • 126:smtp.126.com和pop3.126.com;
    • qq:smtp.qq.com和pop3.qq.com;
    • sohu:smtp.sohu.com和pop3.sohu.com;
    • sina:smtp.sina.com和pop3.sina.com。

    2. Telnet收发邮件

    Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序,用它连接到服务器。终端使用者可以在telnet程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个telnet会话,必须输入用户名和密码来登录服务器。Telnet是常用的远程控制Web服务器的方法

    2.1 BASE64加密

    BASE64是一种加密算法,这种加密方式是可逆的!它的作用是使加密后的文本无法用肉眼识别。Java提供了sun.misc.BASE64Encoder这个类,用来对做Base64的加密和解密,但我们知道,使用sun包下的东西会有警告!甚至在eclipse中根本使用不了这个类(需要设置),所以我们还是听sun公司的话,不要去使用它内部使用的类,我们去使用apache commons组件中的codec包下的Base64这个类来完成BASE64加密和解密。

    package cn.itcast;
    import org.apache.commons.codec.binary.Base64;
    
    public class Base64Utils {
        public static String encode(String s) {
            return encode(s, "utf-8");
        }
    
        public static String decode(String s) {
            return decode(s, "utf-8");
        }
    
        public static String encode(String s, String charset) {
            try {
                byte[] bytes = s.getBytes(charset);
                bytes = Base64.encodeBase64(bytes);
                return new String(bytes, charset);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public static String decode(String s, String charset) {
            try {
                byte[] bytes = s.getBytes(charset);
                bytes = Base64.decodeBase64(bytes);
                return new String(bytes, charset);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    2.2 telnet发邮件

    Xshell是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主机的安全连接以及它创新性的设计和特色帮助用户在复杂的网络环境中享受他们的工作

    Xshell可以在Windows界面下用来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的。

    连接163的smtp服务器

    javamail

    连接成功后需要如下步骤才能发送邮件:

    (1)、与服务器打招呼:ehlo你的名字

    javamail

    (2)、发出登录请求:auth login

    javamail

    (3)、输入加密后的邮箱名:(itcast_cxf@163.com)aXRjYXN0X2N4ZkAxNjMuY29t

    (4)、输入加密后的邮箱密码:(itcast)aXRjYXN0

    javamail

    (5)、输入谁来发送邮件,即from:mail from:itcast_cxf@163.com

    javamail

    (6)、输入把邮件发给谁,即to:rcpt to:itcast_cxf@126.com

    javamail

    (7)、发送填写数据请求:data

    javamail

    (8)、开始输入数据,数据包含:from、to、subject,以及邮件内容,如果输入结束后,以一个“.”为一行,表示输入结束:

    from:<zhangBoZhi@163.com>
    to:<itcast_cxf@sina.com>
    subject: 我爱上你了
    
    我已经深深的爱上你了,我是张柏芝。
    .

    注意,在标题和邮件正文之间要有一个空行!当要退出时,一定要以一个“.”为单行,表示输入结束。

    (9)、最后一步:quit

    javamail

    3. telnet收邮件

    3.1 telnet收邮件的步骤

    pop3无需使用Base64加密

    收邮件连接的服务器是pop3.xxx.com,pop3协议的默认端口号是110。请注意!这与发邮件完全不同。如果你在163有邮箱账户,那么你想使用telnet收邮件,需要连接的服务器是pop3.163.com

    连接pop3服务器:telnet pop3.163.com 110

    命令 功能描述
    user命令 user 用户名,例如:user itcast_cxf@163.com
    pass命令 pass 密码,例如:pass itcast
    stat命令 stat命令用来查看邮箱中邮件的个数,所有邮件所占的空间
    list命令 list命令用来查看所有邮件,或指定邮件的状态,例如:list 1是查看第一封邮件的大小,
    list是查看邮件列表,即列出所有邮件的编号,及大小
    retr命令 查看指定邮件的内容,例如:retr 1#是查看第一封邮件的内容
    dele命令 标记某邮件为删除,但不是马上删除,而是在退出时才会真正删除
    quit命令 退出!如果在退出之前已经使用dele命令标记了某些邮件,那么会在退出是删除它们


    javamail

    javamail

    javamail

    4. JavaMail

    4.1 JavaMail概述

    Java Mail是由SUN公司提供的专门针对邮件的API,主要Jar包:mail.jar、activation.jar。

    在使用MyEclipse创建web项目时,需要小心!如果只是在web项目中使用java mail是没有什么问题的,发布到Tomcat上运行一点问题都没有! 
    但是如果是在web项目中写测试那就出问题了。

    在MyEclipse中,会自动给web项目导入javax.mail包中的类,但是不全(其实是只有接口,而没有接口的实现类),所以只靠MyEclipse中的类是不能运行java mail项目的,但是如果这时你再去自行导入mail.jar时,就会出现冲突。

    处理方案:到下面路径中找到javaee.jar文件,把javax.mail删除!!!

    D:\Program Files\MyEclipse\Common\plugins\com.genuitec.eclipse.j2eedt.core_10.0.0.me201110301321\data\libraryset\EE_5
    

    4.2 JavaMail中主要类

    java mail中主要类:javax.mail.Session、javax.mail.internet.MimeMessage、javax.mail.Transport。

    Session:表示会话,即客户端与邮件服务器之间的会话!想获得会话需要给出账户和密码,当然还要给出服务器名称。在邮件服务中的Session对象,就相当于连接数据库时的Connection对象。

    MimeMessage:表示邮件类,它是Message的子类。它包含邮件的主题(标题)、内容,收件人地址、发件人地址,还可以设置抄送和暗送,甚至还可以设置附件。

    Transport:用来发送邮件。它是发送器!

    4.3 JavaMail之Hello World

    在使用telnet发邮件时,还需要自己来处理Base64编码的问题,但使用JavaMail就不必理会这些问题了,都由JavaMail来处理。

    第一步:获得Session

    Session session = Session.getInstance(Properties prop, Authenticator auth); 
    

    其中prop需要指定两个键值,一个是指定服务器主机名,另一个是指定是否需要认证!我们当然需要认证!

    Properties prop = new Properties();
    prop.setProperty(“mail.host”, “smtp.163.com”);//设置服务器主机名
    prop.setProperty(“mail.smtp.auth”, “true”);//设置需要认证

    其中Authenticator是一个接口表示认证器,即校验客户端的身份。我们需要自己来实现这个接口,实现这个接口需要使用账户和密码。

    Authenticator auth = new Authenticator() {
        public PasswordAuthentication getPasswordAuthentication () {
            new PasswordAuthentication(“itcast_cxf”, “itcast”);//用户名和密码
        }
    };

    通过上面的准备,现在可以获取得Session对象了:

    Session session = Session.getInstance(prop, auth);
    •  

    第二步:创建MimeMessage对象 
    创建MimeMessage需要使用Session对象来创建:

    MimeMessage msg = new MimeMessage(session);
    •  

    然后需要设置发信人地址、收信人地址、主题,以及邮件正文。

    msg.setFrom(new InternetAddress(“itcast_cxf@163.com”));//设置发信人
    msg.addRecipients(RecipientType.TO, “itcast_cxf@qq.com,itcast_cxf@sina.com”);//设置多个收信人
    msg.addRecipients(RecipientType.CC, “itcast_cxf@sohu.com,itcast_cxf@126.com”);//设置多个抄送
    msg.addRecipients(RecipientType.BCC, ”itcast_cxf@hotmail.com”);//设置暗送
    msg.setSubject(“这是一封测试邮件”);//设置主题(标题)
    msg.setContent(“当然是hello world!”, “text/plain;charset=utf-8”);//设置正文

    第三步:发送邮件

    Transport.send(msg);//发送邮件
    •  

    4.4 JavaMail发送带有附件的邮件

    一封邮件可以包含正文、附件N个,所以正文与N个附件都是邮件的一个部份。

    上面的hello world案例中,只是发送了带有正文的邮件!所以在调用setContent()方法时直接设置了正文,如果想发送带有附件邮件,那么需要设置邮件的内容为MimeMultiPart。

    MimeMulitpart parts = new MimeMulitpart();//多部件对象,可以理解为是部件的集合
    msg.setContent(parts);//设置邮件的内容为多部件内容。

    然后我们需要把正文、N个附件创建为“主体部件”对象(MimeBodyPart),添加到MimeMuiltPart中即可。

    MimeBodyPart part1 = new MimeBodyPart();//创建一个部件
    part1.setCotnent(“这是正文部分”, “text/html;charset=utf-8”);//给部件设置内容
    parts.addBodyPart(part1);//把部件添加到部件集中。
    

    下面我们创建一个附件:

    MimeBodyPart part2 = new MimeBodyPart();//创建一个部件
    part2.attachFile(“F:\\a.jpg”);//设置附件
    part2.setFileName(“hello.jpg”);//设置附件名称
    parts.addBodyPart(part2);//把附件添加到部件集中

    注意,如果在设置文件名称时,文件名称中包含了中文的话,那么需要使用MimeUitlity类来给中文编码:

    part2.setFileName(MimeUitlity.encodeText(“美女.jpg”));
    •  

    JavaMail

    public class JavaMailDemo {
    
      public void sendMail() throws Exception {
        /*
         * 1. 得到session
         */
        Properties props = new Properties();
        props.setProperty("mail.host", "smtp.163.com");
        props.setProperty("mail.smtp.auth", "true");
    
        Authenticator auth = new Authenticator() {
          @Override
          protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("itcast_cxf", "itcast");
          }
        };
    
        Session session = Session.getInstance(props, auth);
    
        /*
         * 2. 创建MimeMessage
         */
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress("itcast_cxf@163.com"));//设置发件人
        msg.setRecipients(RecipientType.TO, "itcast_cxf@126.com");//设置收件人
        msg.setRecipients(RecipientType.CC, "itcast_cxf@sohu.com");//设置抄送
        msg.setRecipients(RecipientType.BCC, "itcast_cxf@sina.com");//设置暗送
    
        msg.setSubject("这是来自ITCAST的测试邮件");
        msg.setContent("这就是一封垃圾邮件!", "text/html;charset=utf-8");
    
        /*
         * 3. 发邮件
         */
        Transport.send(msg);
      }
    
      /**
       * 带有附件的邮件!!!
       */
    
      public void sendMail2() throws Exception {
        /*
         * 1. 得到session
         */
        Properties props = new Properties();
        props.setProperty("mail.host", "smtp.163.com");
        props.setProperty("mail.smtp.auth", "true");
    
        Authenticator auth = new Authenticator() {
          @Override
          protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("itcast_cxf", "itcast");
          }
        };
    
        Session session = Session.getInstance(props, auth);
    
        /*
         * 2. 创建MimeMessage
         */
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress("itcast_cxf@163.com"));//设置发件人
        msg.setRecipients(RecipientType.TO, "itcast_cxf@126.com");//设置收件人
    
        msg.setSubject("这是来自ITCAST的测试邮件有附件");
    
    
        
        /*
         * 当发送包含附件的邮件时,邮件体就为多部件形式!
         * 1. 创建一个多部件的部件内容!MimeMultipart
         *   MimeMultipart就是一个集合,用来装载多个主体部件!
         * 2. 我们需要创建两个主体部件,一个是文本内容的,另一个是附件的。
         *   主体部件叫MimeBodyPart
         * 3. 把MimeMultipart设置给MimeMessage的内容!
         */
        MimeMultipart list = new MimeMultipart();//创建多部分内容
    
        // 创建MimeBodyPart
        MimeBodyPart part1 = new MimeBodyPart();
        // 设置主体部件的内容
        part1.setContent("这是一封包含附件的垃圾邮件", "text/html;charset=utf-8");
        // 把主体部件添加到集合中
        list.addBodyPart(part1);
    
    
        // 创建MimeBodyPart
        MimeBodyPart part2 = new MimeBodyPart();
        part2.attachFile(new File("F:/f/白冰.jpg"));//设置附件的内容
        //设置显示的文件名称,其中encodeText用来处理中文乱码问题
        part2.setFileName(MimeUtility.encodeText("大美女.jpg"));
        list.addBodyPart(part2);
    
        msg.setContent(list);//把它设置给邮件作为邮件的内容。
    
    
        
    
        /*
         * 3. 发邮件
         */
        Transport.send(msg);    
      }
    
      public void sendMail3() throws Exception {
        /*
         * 1. 得到session
         */
        Session session = MailUtils.createSession("smtp.163.com", 
            "itcast_cxf", "itcast");
        /*
         * 2. 创建邮件对象
         */
        Mail mail = new Mail("itcast_cxf@163.com",
            "itcast_cxf@126.com,itcast_cxf@sina.com",
            "不是垃圾邮件能是什么呢?", "这里是正文");
    
        /*
         * 创建两个附件对象
         */
        AttachBean ab1 = new AttachBean(new File("F:/f/白冰.jpg"), "小美女.jpg");
        AttachBean ab2 = new AttachBean(new File("F:/f/big.jpg"), "我的羽绒服.jpg");
    
        // 添加到mail中
        mail.addAttach(ab1);
        mail.addAttach(ab2);
    
        /*
         * 3. 发送
         */
        MailUtils.send(session, mail);
      }
    }

    5. MailUtils

    public class MailUtils {
        public static Session createSession(String host, final String username, final String password) {
            Properties prop = new Properties();
            prop.setProperty("mail.host", host);// 指定主机
            prop.setProperty("mail.smtp.auth", "true");// 指定验证为true
    
            // 创建验证器
            Authenticator auth = new Authenticator() {
                public PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            };
    
            // 获取session对象
            return Session.getInstance(prop, auth);
        }
    
        /**
         * 发送指定的邮件
         * 
         * @param mail
         */
        public static void send(Session session, final Mail mail) throws MessagingException,
                IOException {
    
            MimeMessage msg = new MimeMessage(session);// 创建邮件对象
            msg.setFrom(new InternetAddress(mail.getFrom()));// 设置发件人
            msg.addRecipients(RecipientType.TO, mail.getToAddress());// 设置收件人
    
            // 设置抄送
            String cc = mail.getCcAddress();
            if (!cc.isEmpty()) {
                msg.addRecipients(RecipientType.CC, cc);
            }
    
            // 设置暗送
            String bcc = mail.getBccAddress();
            if (!bcc.isEmpty()) {
                msg.addRecipients(RecipientType.BCC, bcc);
            }
    
            msg.setSubject(mail.getSubject());// 设置主题
    
            MimeMultipart parts = new MimeMultipart();// 创建部件集对象
    
            MimeBodyPart part = new MimeBodyPart();// 创建一个部件
            part.setContent(mail.getContent(), "text/html;charset=utf-8");// 设置邮件文本内容
            parts.addBodyPart(part);// 把部件添加到部件集中
    
            ///
    
            // 添加附件
            List<AttachBean> attachBeanList = mail.getAttachs();// 获取所有附件
            if (attachBeanList != null) {
                for (AttachBean attach : attachBeanList) {
                    MimeBodyPart attachPart = new MimeBodyPart();// 创建一个部件
                    attachPart.attachFile(attach.getFile());// 设置附件文件
                    attachPart.setFileName(MimeUtility.encodeText(attach
                            .getFileName()));// 设置附件文件名
                    parts.addBodyPart(attachPart);
                }
            }
    
            msg.setContent(parts);// 给邮件设置内容
            Transport.send(msg);// 发邮件
        }
    }
    文章来自:https://blog.csdn.net/axi295309066/article/details/52985134
    展开全文
  • 利用java实现发送邮件

    万次阅读 多人点赞 2018-11-04 17:44:53
    很显然这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发送邮件。电子邮件协议 电子邮件在网络中传输和网页...

    电子邮件的应用非常广泛,常见的如在某网站注册了一个账户,自动发送一封激活邮件,通过邮件找回密码,自动批量发送活动信息等。很显然这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发送邮件。

    电子邮件协议
    电子邮件在网络中传输和网页一样需要遵从特定的协议,常用的电子邮件协议包括 SMTP,POP3,IMAP。其中邮件的创建和发送只需要用到 SMTP协议,所以本文也只会涉及到SMTP协议。SMTP 是 Simple Mail Transfer Protocol 的简称,即简单邮件传输协议。
    JavaMail
    我们平时通过 Java 代码打开一个 http 网页链接时,通常可以使用已经对 http 协议封装好的 HttpURLConnection 类来快速地实现。Java 官方也提供了对电子邮件协议封装的 Java 类库,就是JavaMail,但并没有包含到标准的 JDK 中,需要我们自己去官方下载,这里我从 JavaEE 官方的 Github 仓库下载。
    JavaMail 下载地址:https://github.com/javaee/javamail/releases
    最新的版本是 1.6.0 ,包含了 SMTP, IMAP, 和 POP3 协议的实现:

    如果是mvn项目的话,我们只需要添加依赖即可
    <dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.5.0-b01</version>
    </dependency>
    创建一封简单的电子邮件
    邮件创建步骤:
    1. 创建一个邮件对象(MimeMessage);
    2. 设置发件人,收件人,可选增加多个收件人,抄送人,密送人;
    3. 设置邮件的主题(标题);
    4. 设置邮件的正文(内容);
    5. 设置显示的发送时间;
    6. 保存到本地。
    代码如下:

     

    public class TestMail {
        public static void main(String[] args) throws Exception {
            // 1. 创建一封邮件
            Properties props = new Properties();                // 用于连接邮件服务器的参数配置(发送邮件时才需要用到)
            Session session= Session.getInstance(props);        // 根据参数配置,创建会话对象(为了发送邮件准备的)
            MimeMessage message = new MimeMessage(session);     // 创建邮件对象
    
            /*
             * 也可以根据已有的eml邮件文件创建 MimeMessage 对象
             * MimeMessage message = new MimeMessage(session, new FileInputStream("myEmail.eml"));
             */
    
            // 2. From: 发件人
            //    其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
            //    真正要发送时, 邮箱必须是真实有效的邮箱。
            message.setFrom(new InternetAddress("email@send.com", "USER_AA", "UTF-8"));
    
            // 3. To: 收件人
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress("cc@receive.com", "USER_CC", "UTF-8"));
            //    To: 增加收件人(可选)
            message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("dd@receive.com", "USER_DD", "UTF-8"));
            //    Cc: 抄送(可选)
            message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress("ee@receive.com", "USER_EE", "UTF-8"));
            //    Bcc: 密送(可选)
            message.setRecipient(MimeMessage.RecipientType.BCC, new InternetAddress("ff@receive.com", "USER_FF", "UTF-8"));
    
            // 4. Subject: 邮件主题
            message.setSubject("邮件主题", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)
            message.setContent("这是邮件正文", "text/html;charset=UTF-8");
    
            // 6. 设置显示的发件时间
            message.setSentDate(new Date());
    
            // 7. 保存前面的设置
            message.saveChanges();
    
            // 8. 将该邮件保存到本地
            OutputStream out = new FileOutputStream("myEmail.eml");
            message.writeTo(out);
            out.flush();
            out.close();
        }
    }

    保存的 myEmail.eml 可以使用邮件客户端打开查看,实际上就是一堆符合SMTP协议格式的文本(内容使用base64进行了编码),也可用记事本打开,如下所示:

    发送电子邮件
    发送邮件首先需要有一个邮箱账号和密码,本文以网易126邮箱为例,邮箱账号必须要开启 SMTP 服务,在浏览器网页登录邮箱后一般在邮箱的“设置”选项中可以开启,并记下邮箱的 SMTP 服务器地址,如下所示(其他邮箱大同小异):

    代码实现:

     

    public class SendMail {
        // 发件人的 邮箱 和 密码(替换为自己的邮箱和密码)
        // PS: 某些邮箱服务器为了增加邮箱本身密码的安全性,给 SMTP 客户端设置了独立密码(有的邮箱称为“授权码”),
        //     对于开启了独立密码的邮箱, 这里的邮箱密码必需使用这个独立密码(授权码)。
        public static String myEmailAccount = "xxxxxxx@126.com";
        public static String myEmailPassword = "xxxxxxxx";
    
        // 发件人邮箱的 SMTP 服务器地址, 必须准确, 不同邮件服务器地址不同, 一般(只是一般, 绝非绝对)格式为: smtp.xxx.com
        // 网易126邮箱的 SMTP 服务器地址为: smtp.126.com
        public static String myEmailSMTPHost = "smtp.126.com";
    
        // 收件人邮箱(替换为自己知道的有效邮箱)
        public static String receiveMailAccount = "xxxxxx@qq.com";
    
        public static void main(String[] args) throws Exception {
            // 1. 创建参数配置, 用于连接邮件服务器的参数配置
            Properties props = new Properties();                    // 参数配置
            props.setProperty("mail.transport.protocol", "smtp");   // 使用的协议(JavaMail规范要求)
            props.setProperty("mail.smtp.host", myEmailSMTPHost);   // 发件人的邮箱的 SMTP 服务器地址
            props.setProperty("mail.smtp.auth", "true");            // 需要请求认证
    
            // PS: 某些邮箱服务器要求 SMTP 连接需要使用 SSL 安全认证 (为了提高安全性, 邮箱支持SSL连接, 也可以自己开启),
            //     如果无法连接邮件服务器, 仔细查看控制台打印的 log, 如果有有类似 “连接失败, 要求 SSL 安全连接” 等错误,
            //     取消下面 /* ... */ 之间的注释代码, 开启 SSL 安全连接。
            /*
            // SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果开启了 SSL 连接,
            //                  需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应邮箱服务的帮助,
            //                  QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
            final String smtpPort = "465";
            props.setProperty("mail.smtp.port", smtpPort);
            props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            props.setProperty("mail.smtp.socketFactory.fallback", "false");
            props.setProperty("mail.smtp.socketFactory.port", smtpPort);
            */
    
            // 2. 根据配置创建会话对象, 用于和邮件服务器交互
            Session session = Session.getInstance(props);
            // 设置为debug模式, 可以查看详细的发送 log
            session.setDebug(true);
    
            // 3. 创建一封邮件
            MimeMessage message = createMimeMessage(session, myEmailAccount, receiveMailAccount);
    
            // 4. 根据 Session 获取邮件传输对象
            Transport transport = session.getTransport();
    
            // 5. 使用 邮箱账号 和 密码 连接邮件服务器, 这里认证的邮箱必须与 message 中的发件人邮箱一致, 否则报错
            //
            //    PS_01: 如果连接服务器失败, 都会在控制台输出相应失败原因的log。
            //    仔细查看失败原因, 有些邮箱服务器会返回错误码或查看错误类型的链接,
            //    根据给出的错误类型到对应邮件服务器的帮助网站上查看具体失败原因。
            //
            //    PS_02: 连接失败的原因通常为以下几点, 仔细检查代码:
            //           (1) 邮箱没有开启 SMTP 服务;
            //           (2) 邮箱密码错误, 例如某些邮箱开启了独立密码;
            //           (3) 邮箱服务器要求必须要使用 SSL 安全连接;
            //           (4) 请求过于频繁或其他原因, 被邮件服务器拒绝服务;
            //           (5) 如果以上几点都确定无误, 到邮件服务器网站查找帮助。
            //
            transport.connect(myEmailAccount, myEmailPassword);
    
            // 6. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
            transport.sendMessage(message, message.getAllRecipients());
    
            // 7. 关闭连接
            transport.close();
        }
    
        /**
         * 创建一封只包含文本的简单邮件
         *
         * @param session     和服务器交互的会话
         * @param sendMail    发件人邮箱
         * @param receiveMail 收件人邮箱
         * @return
         * @throws Exception
         */
        public static MimeMessage createMimeMessage(Session session, String sendMail, String receiveMail) throws Exception {
            // 1. 创建一封邮件
            MimeMessage message = new MimeMessage(session);
    
            // 2. From: 发件人
            message.setFrom(new InternetAddress(sendMail, "昵称", "UTF-8"));
    
            // 3. To: 收件人(可以增加多个收件人、抄送、密送)
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(receiveMail, "XX用户", "UTF-8"));
    
            // 4. Subject: 邮件主题
            message.setSubject("主题", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)
            message.setContent("邮件正文", "text/html;charset=UTF-8");
                // 6. 设置发件时间
            message.setSentDate(new Date());
    
            // 7. 保存设置
            message.saveChanges();
    
            return message;
        }
    }

    下面我们对我们的代码解析一下:
    1、对应用程序配置邮件会话
    javax.mail.Session保存邮件系统的配置属性和提供用户验证的信息,发送email首先要获取session对象。
    (1)Session.getInstance(java.util.Properties)获取非共享的session对象
    (2)Session.getDefaultInstance(java.utilProperties)获取共享的session对象
    两者都必须建立Properties prop=new Properties()对象;
    注意:一般对单用户桌面应用程序使用共享Session对象。
    用SMTP协议发送Email时通常要设置mail.smtp.host(mail.protocol.host协议特定邮件服务器名)属性。

    prop.put("mail.smtp.host","smtp.mailServer.com");
    Session mailSession=Session.getInstance(prop);

    注意:在真正使用创建的过程中,往往会让我们验证密码,这是我们要写一个密码验证类。javax.mail.Authenticator是一个抽象类,我们要写MyAuthenticator的密码验证类,该类继承Authenticator实现:

    protected PasswordAuthentication getPasswordAuthentication(){
    return new PasswordAuthentication(String userName, String password);
    }

    这时我们创建Session对象:
    Session mailSession=Session.getInstance(prop,new MyAuthenticator(userName,Password));
    并且要设置使用验证:prop.put("mail.smtp.auth","true");
    使用 STARTTLS安全连接:prop.put("mail.smtp.starttls.enable","true");
    2、配置邮件会话之后,要编写消息
    要编写消息就要生成javax.mail.Message子类的实例或对Internet邮件使用javax.mail.interet.MimeMessage类。
    (1)建立MimeMessage对象
    MimeMessage扩展抽象的Message类,构造MimeMessage对象:
    MimeMessage message=new MimeMessage(mailSession);
    (2)消息发送者、日期、主题

    message.setFrom(Address theSender);
    message.setSentDate(java.util.Date theDate);
    message.setSubject(String theSubject);

    (3)设置消息的接受者与发送者(寻址接收)

    setRecipient(Message.RecipientType type , Address theAddress);
    setRecipients(Message.RecipientType type , Address[] theAddress);
    addRecipient(Message.RecipientType type , Address theAddress);
    addRecipients(Message.RecipientType type,Address[] theAddress);

    方法都可以指定接受者类型,但是一般用后两个,这样可以避免意外的替换或者覆盖接受者名单。定义接受者类型:
    Message.RecipientType.TO:消息接受者
    Message.RecipientType.CC:消息抄送者
    Message.RecipientType.BCC:匿名抄送接收者(其他接受者看不到这个接受者的姓名和地址)
    (4)设置消息内容
    JavaMail基于JavaBean Activation FrameWork(JAF),JAF可以构造文本消息也可以支持附件。
    设置消息内容时,要提供消息的内容类型-----即方法签名:
    MimeMessage.setContent(Object theContent,String type);
    也可以不用显式的制定消息的内容类型:MimeMessage.setText(String theText);

     

    注意:建立地址javax.mail.InternetAddress toAddress=new InternetAddress(String address);

     

     

    源码

    参考:

    http://blog.csdn.net/qq_32371887/article/details/72821291

    http://www.cnblogs.com/codeplus/archive/2011/10/30/2229391.html

    http://blog.csdn.net/xietansheng/article/details/51673073

    http://blog.csdn.net/xietansheng/article/details/51722660

    http://www.cnblogs.com/jiaguozhilian/p/5809815.html

    http://blog.csdn.net/karem/article/details/4646071

    http://blog.csdn.net/csh624366188/article/details/7183457

    http://www.cnblogs.com/Ant-soldier/p/6592858.html

    http://blog.csdn.net/shuaicihai/article/details/59484175

     

     

     

    展开全文
  • 基于JavaMail的Java邮件发送:简单邮件发送

    万次阅读 多人点赞 2019-01-24 11:59:59
    本文链接: ... ...但这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发...

    本文链接: https://blog.csdn.net/xietansheng/article/details/51673073

    电子邮件的应用非常广泛,例如在某网站注册了一个账户,自动发送一封欢迎邮件,通过邮件找回密码,自动批量发送活动信息等。但这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发送邮件。

    1. 电子邮件协议

    电子邮件的在网络中传输和网页一样需要遵从特定的协议,常用的电子邮件协议包括 SMTP,POP3,IMAP。其中邮件的创建和发送只需要用到 SMTP协议,所以本文也只会涉及到SMTP协议。SMTP 是 Simple Mail Transfer Protocol 的简称,即简单邮件传输协议。

    2. JavaMail

    我们平时通过 Java 代码打开一个 http 网页链接时,通常可以使用已经对 http 协议封装好的 HttpURLConnection 类来快速地实现。Java 官方也提供了对电子邮件协议封装的 Java 类库,就是JavaMail,但并没有包含到标准的 JDK 中,需要我们自己去官方下载,这里我从 JavaEE 官方的 Github 仓库下载。

    JavaMail 下载地址: https://github.com/javaee/javamail/releases

    这里我下载的版本是 1.6.0 版本,包含了 SMTP, IMAP, 和 POP3 协议的实现的 jar 包:

    javaxmail.png

    我把 JavaMail 1.6.0 的 jar 包上传到了 CSDN,如果无法从 Github 下载,可以从 CSDN 下载(建议到Github下载,CSDN上我设了积分): http://download.csdn.net/download/xietansheng/9928266

    特别注意:

    • 本测试用例用的 JavaMail 版本是 1.6.0,如果下载到其他版本的 JavaMail 运行时出现问题,请使用 JavaMail 1.6.0 版本再进行尝试。
    • 使用 JavaMail 1.6.0 要求的 JDK 版本必须是 JDK 1.7 以上(建议使用最新版 JDK)。
    • 不要直接就完完全全复制我的代码,需要 修改一下发送的标题、内容、用户昵称,要不然所有人都直接复制我的代码发送(自己也不要反复发送内容相同的邮件),内容一致的邮件多次发送,邮箱服务器就可能会检测到这些内容是垃圾广告内容,不让你发送,会返回错误码,查询错误码也能查询到失败原因。

    有些童鞋反应代码提示某些类找不到,或运行出现问题,往往都是 JDK 版本过低,或 JavaMail 版本过低,出现问题时 请使用推荐的版本

    再出现问题,只要能连接成功,都有错误码返回,有些童鞋发送失败在控制台已经给出了错误码,大部分还给出了查看错误码含义的链接,点开链接,查询对应的错误码,为什么失败,已经明明白白的告诉你了,针对失败原因修改即可!

    邮件发送,涉及多端(本地代码端、邮件发送服务器端、邮件接收服务器端),保证自己的代码没问题了,不一定就能成功,你把你的邮件提交到邮件发送服务器,发送的服务器可以给你拒绝服务(比如认为发送的内容是垃圾广告,或者你频繁请求发送),这不是代码端能管的事情,但都有错误码返回,明确告诉你为什么错误。就算你发送成功了,对方也有可能接收不到,成功发送到对方的邮件接收服务器后,对方的服务器可以根据你的内容拒绝收你的邮件(比如认为你的内容是广告诈骗等信息,或者发送过于频繁),对方的服务器可能直接把你的邮件扔垃圾箱,或者直接忽略。出现这些问题,请修改发送的标题、内容、昵称等,重复的内容不要反复发送,或多更换收件箱和发件箱试试!!!

    3. 创建一封简单的电子邮件

    首先创建一个 Java 工程,把下载好的 javax.mail.jar 作为类库加入工程,这里不多说。

    邮件创建步骤:

    1. 创建一个邮件对象(MimeMessage);
    2. 设置发件人,收件人,可选增加多个收件人,抄送人,密送人;
    3. 设置邮件的主题(标题);
    4. 设置邮件的正文(内容);
    5. 设置显示的发送时间;
    6. 保存到本地。

    代码实现:

    package com.xiets.javamaildemo;
    
    import javax.mail.Session;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    import java.util.Date;
    import java.util.Properties;
    
    /**
     * JavaMail 版本: 1.6.0
     * JDK 版本: JDK 1.7 以上(必须)
     */
    public class Main {
    
        public static void main(String[] args) throws Exception {
            // 1. 创建一封邮件
            Properties props = new Properties();
            // 用于连接邮件服务器的参数配置(发送邮件时才需要用到)
            Session session= Session.getInstance(props);        // 根据参数配置,创建会话对象(为了发送邮件准备的)
            MimeMessage message = new MimeMessage(session);     // 创建邮件对象
    
            /*
             * 也可以根据已有的eml邮件文件创建 MimeMessage 对象
             * MimeMessage message = new MimeMessage(session, new FileInputStream("MyEmail.eml"));
             */
    
            // 2. From: 发件人
            //    其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
            //    真正要发送时, 邮箱必须是真实有效的邮箱。
            message.setFrom(new InternetAddress("aa@send.com", "USER_AA", "UTF-8"));
    
            // 3. To: 收件人
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress("cc@receive.com", "USER_CC", "UTF-8"));
            //    To: 增加收件人(可选)
            message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("dd@receive.com", "USER_DD", "UTF-8"));
            //    Cc: 抄送(可选)
            message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress("ee@receive.com", "USER_EE", "UTF-8"));
            //    Bcc: 密送(可选)
            message.setRecipient(MimeMessage.RecipientType.BCC, new InternetAddress("ff@receive.com", "USER_FF", "UTF-8"));
    
            // 4. Subject: 邮件主题
            message.setSubject("TEST邮件主题", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)
            message.setContent("TEST这是邮件正文。。。", "text/html;charset=UTF-8");
    
            // 6. 设置显示的发件时间
            message.setSentDate(new Date());
    
            // 7. 保存前面的设置
            message.saveChanges();
    
            // 8. 将该邮件保存到本地
            OutputStream out = new FileOutputStream("MyEmail.eml");
            message.writeTo(out);
            out.flush();
            out.close();
        }
    
    }
    

    保存的 MyEmail.eml 可以使用邮件客户端打开查看,实际上就是一堆符合SMTP协议格式的文本(内容使用base64进行了编码),也可用记事本打开,如下所示:

    my-email.png

    my-email-txt.png

    4. 发送电子邮件

    发送邮件首先需要有一个邮箱账号和密码,本文以网易163邮箱为例,邮箱账号必须要开启 SMTP 服务,在浏览器网页登录邮箱后一般在邮箱的“设置”选项中可以开启,并记下邮箱的 SMTP 服务器地址,如下所示(其他邮箱大同小异):

    smtp-163-setting.jpg

    代码实现:

    package com.xiets.javamaildemo;
    
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.util.Date;
    import java.util.Properties;
    
    /**
     * JavaMail 版本: 1.6.0
     * JDK 版本: JDK 1.7 以上(必须)
     */
    public class Main {
    
        // 发件人的 邮箱 和 密码(替换为自己的邮箱和密码)
        // PS: 某些邮箱服务器为了增加邮箱本身密码的安全性,给 SMTP 客户端设置了独立密码(有的邮箱称为“授权码”), 
        //     对于开启了独立密码的邮箱, 这里的邮箱密码必需使用这个独立密码(授权码)。
        public static String myEmailAccount = "xxxxxxxxx@163.com";
        public static String myEmailPassword = "xxxxxxxxx";
    
        // 发件人邮箱的 SMTP 服务器地址, 必须准确, 不同邮件服务器地址不同, 一般(只是一般, 绝非绝对)格式为: smtp.xxx.com
        // 网易163邮箱的 SMTP 服务器地址为: smtp.163.com
        public static String myEmailSMTPHost = "smtp.163.com";
    
        // 收件人邮箱(替换为自己知道的有效邮箱)
        public static String receiveMailAccount = "xxxxxxxxx@qq.com";
    
        public static void main(String[] args) throws Exception {
            // 1. 创建参数配置, 用于连接邮件服务器的参数配置
            Properties props = new Properties();                    // 参数配置
            props.setProperty("mail.transport.protocol", "smtp");   // 使用的协议(JavaMail规范要求)
            props.setProperty("mail.smtp.host", myEmailSMTPHost);   // 发件人的邮箱的 SMTP 服务器地址
            props.setProperty("mail.smtp.auth", "true");            // 需要请求认证
    
            // PS: 某些邮箱服务器要求 SMTP 连接需要使用 SSL 安全认证 (为了提高安全性, 邮箱支持SSL连接, 也可以自己开启),
            //     如果无法连接邮件服务器, 仔细查看控制台打印的 log, 如果有有类似 “连接失败, 要求 SSL 安全连接” 等错误,
            //     打开下面 /* ... */ 之间的注释代码, 开启 SSL 安全连接。
            /*
            // SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果开启了 SSL 连接,
            //                  需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应邮箱服务的帮助,
            //                  QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
            final String smtpPort = "465";
            props.setProperty("mail.smtp.port", smtpPort);
            props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            props.setProperty("mail.smtp.socketFactory.fallback", "false");
            props.setProperty("mail.smtp.socketFactory.port", smtpPort);
            */
    
            // 2. 根据配置创建会话对象, 用于和邮件服务器交互
            Session session = Session.getInstance(props);
            session.setDebug(true);                                 // 设置为debug模式, 可以查看详细的发送 log
    
            // 3. 创建一封邮件
            MimeMessage message = createMimeMessage(session, myEmailAccount, receiveMailAccount);
    
            // 4. 根据 Session 获取邮件传输对象
            Transport transport = session.getTransport();
    
            // 5. 使用 邮箱账号 和 密码 连接邮件服务器, 这里认证的邮箱必须与 message 中的发件人邮箱一致, 否则报错
            // 
            //    PS_01: 成败的判断关键在此一句, 如果连接服务器失败, 都会在控制台输出相应失败原因的 log,
            //           仔细查看失败原因, 有些邮箱服务器会返回错误码或查看错误类型的链接, 根据给出的错误
            //           类型到对应邮件服务器的帮助网站上查看具体失败原因。
            //
            //    PS_02: 连接失败的原因通常为以下几点, 仔细检查代码:
            //           (1) 邮箱没有开启 SMTP 服务;
            //           (2) 邮箱密码错误, 例如某些邮箱开启了独立密码;
            //           (3) 邮箱服务器要求必须要使用 SSL 安全连接;
            //           (4) 请求过于频繁或其他原因, 被邮件服务器拒绝服务;
            //           (5) 如果以上几点都确定无误, 到邮件服务器网站查找帮助。
            //
            //    PS_03: 仔细看log, 认真看log, 看懂log, 错误原因都在log已说明。
            transport.connect(myEmailAccount, myEmailPassword);
    
            // 6. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
            transport.sendMessage(message, message.getAllRecipients());
    
            // 7. 关闭连接
            transport.close();
        }
    
        /**
         * 创建一封只包含文本的简单邮件
         *
         * @param session 和服务器交互的会话
         * @param sendMail 发件人邮箱
         * @param receiveMail 收件人邮箱
         * @return
         * @throws Exception
         */
        public static MimeMessage createMimeMessage(Session session, String sendMail, String receiveMail) throws Exception {
            // 1. 创建一封邮件
            MimeMessage message = new MimeMessage(session);
    
            // 2. From: 发件人(昵称有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改昵称)
            message.setFrom(new InternetAddress(sendMail, "某宝网", "UTF-8"));
    
            // 3. To: 收件人(可以增加多个收件人、抄送、密送)
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(receiveMail, "XX用户", "UTF-8"));
    
            // 4. Subject: 邮件主题(标题有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改标题)
            message.setSubject("打折钜惠", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)(内容有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改发送内容)
            message.setContent("XX用户你好, 今天全场5折, 快来抢购, 错过今天再等一年。。。", "text/html;charset=UTF-8");
    
            // 6. 设置发件时间
            message.setSentDate(new Date());
    
            // 7. 保存设置
            message.saveChanges();
    
            return message;
        }
    
    }
    

    发送后查看收件人的收件箱:

    sended-email.jpg


    包含文本、图片、附件 的复杂邮件的创建请看下一篇:基于 JavaMail 的 Java 邮件发送:复杂邮件发送


    展开全文
  • Java 发送邮件的几种方式

    万次阅读 多人点赞 2018-10-24 14:53:38
    发送文件的项目地址(free):... 导入jar包:activation-1.1.jar javax.mail-1.6.2.jar 内容:1.发送一封只包含文本的简单邮件 SendEmail_text.java  2.发送包含内嵌图片的邮件 Send...
  • java发送简单邮件

    2019-05-28 14:48:56
    java发送简单邮件简介JavaMail所需要的Maven的jar包代码实现成果展示 简介 java发送邮件是通过javaMail来实现的。JavaMail根据百度百科的解释是提供给开发者处理电子邮件相关的编程接口。JavaMail是Sun公司发布的...
  • 1. 导入javamail for Android javamail for Android是javamail兼容Android的版本,在build.gradle(Module: app)中dependencies块下加入以下依赖即可使用 // javamail[2019.2.19] implementation '...
  • 1,导入依赖 <dependency> <groupId>com.sun.mail</groupId> <artifactId>jakarta.mail</artifactId> <version>1.6.3</version> </dependency>...
  • 介绍了使用javamail发送电子邮件时所需要使用的类,还有代码示例
  • javamail 发送邮件

    千次阅读 2019-11-08 13:41:19
    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.....
  • javaEmail发送邮件

    千次阅读 2019-03-28 11:58:49
    效果如下: 一.电子邮件协议: SMTP 简单邮件传输 SMTP是Simple Mail Transfer Protocol的简称,即简单邮件传输协议。该协议定义了邮件客户端软件和SMTP服务器之间,以及两台SMTP服务器之间的通信规则。...
  • Java邮件发送详解

    万次阅读 多人点赞 2019-08-13 09:06:30
    电子邮件 要在网络上实现邮件功能,必须要有专门的邮件服务器。 这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。 ... 电子邮箱(E-Mail地址)的获得...
  • Java Mail(二):JavaMail介绍及发送一封简单邮件

    万次阅读 多人点赞 2018-01-05 12:07:14
     JavaMail是SUN提供给开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发类库,支持常用的邮件协议,如SMTP、POP3、IMAP,开发人员使用JavaMail编写邮件程序时,无需考虑底层的通信细节(Socket)
  • Fundamentals of the JavaMail API Presented by developerWorks, your source for great tutorials ibm.com/developerWorks Table of Contents If you're viewing this document online, you can click ...
  • JavaMail API中定义了一个javax.mail.Store类,它用于执行邮件接收任务,这个类的实例对象封装了某种邮件接收协议的底层实施细节,应用程序调用这个类中的方法就可以获得用户邮箱中的各个邮件夹的信息。JavaMail使用...
  • javaMail
  • javamail发送邮件、下载邮件核心功能

    万次阅读 2017-11-16 15:55:18
    common-email 之前一直在做邮件系统,涉及到邮件发送(直发、代发)、邮件下载(国内、国外)、服务商配置、代理(主要针对国外)、日志收集、消息通知、分布式锁、水平拆分等功能, 由于涉及公司隐私问题,在这里提供...
  • Javamail 发送163邮件

    千次阅读 2018-04-03 16:58:59
    1. 需要用的包jar包 javaMail; 可以从GIT上下载,我这里用到的1.6 https://github.com/javaee/javamail/releases2. 开启163的授权码,进入到如下图,按照上面显示的操作就行了 特别说明: 使用发送和认证的邮箱要是同...
  • 在使用javamail的时候,有时我们会打开debug,让javamail输出debug日志。但是,javamail默认是输出到System.out中,如果应用使用的是log4j或logback之类的日志框架,如何将javamail的debug日志也统一由这些日志框架...
  • 06. JavaMail 转发邮件

    2018-03-05 16:50:45
    JavaMail 转发邮件JavaMail API来转发电子邮件,基本的步骤如下:获取Session对象与POP和SMTP服务器的细节的属性。我们需要的POP细节来检索信息和SMPT详细信息发送邮件;创建POP3存储对象,并连接到存储;创建文件夹...
1 2 3 4 5 ... 20
收藏数 27,507
精华内容 11,002
关键字:

javamail