JavaMail


电子邮件是一种异步通信方式,电子邮件把邮件发送到收件人使用的邮件服务器,并放在收件人邮箱中,收件人可以随时上网到自己使用的邮件服务器进行读取。

一个电子邮件系统主要有三个构建,分别为用户代理、邮件服务器和电子邮件协议(SMTP、POP3、IMAP)。

 

用户代理:用户与电子邮件系统的接口,用户代理使使用户能够通过一个友好的接口来发送和接收邮件,如Outlook、Foxmail等。

邮件服务器:电子邮件系统的核心。邮件服务器的功能是发送和接收邮件,同时还要向发信人报告邮件的传送情况(已交付、被拒绝、丢失等)。

邮件服务器采用客户-服务器的方式工作,但它同时充当客户和服务器。例如,当邮件服务器A向邮件服务器B发送邮件时,A作为SMTP客户,而B是SMTP服务器;反之,当B向A发送邮件时,B就是SMTP客户,而A就是SMTP服务器。

协议:邮件发送协议用户用户代理向邮件服务器发送邮件或在邮件服务器之间发送邮件,通常使用的是SMTP(SMTP采用“推”的通信方式,即用户代理向邮件服务器发送邮件或邮件服务器之间发送邮件时,SMTP客户端主动将邮件“推”送到SMTP服务器端);邮件读取协议用于用户代理从邮件服务器读取邮件,如POP3(POP3 采用的是“拉”的通信方式,当用户读取邮件时,用户代理向邮件服务器发出请求。“拉”取用户邮箱中的邮件)。

 

下面简单总结下电子邮件的收发过程:

1)       发信人调用用户代理来编辑发送的邮件,用户代理采用SMTP协议将邮件发送给发送方邮件服务器;

2)       发送方邮件服务器将邮件缓存到队列中,等待发送;

3)       运行在发送方邮件服务器的SMTP客户进程,发现有待发送的邮件,就向运行在接收方邮件服务器的SMTP服务进程发起建立TCP连接;

4)       TCP连接建立完毕后,SMTP客户端向SMTP服务器发送邮件,邮件发送完毕,关闭TCP连接;

5)       运行在接收方邮件服务器中的SMTP服务器进程接收到邮件后,将邮件放入收信人的用户邮箱中,等待收信容人在方便时读取;

6)       收信人调用客户代理,采用POP3 或IMAP协议将自己的邮件从接收方邮件服务器的用户邮箱中取回。

电子邮件格式

一个电子邮件分为信封和内容两大部分。邮件内容又分为首部和主体两部分。[RFC 822]规定了邮件的首部格式,而邮件的主体部分则让用户自由撰写。用户写好首部后,邮件系统会自动将信封所需要的信息提取出来并写在信封上,用户不需要填写信封上的信息。

邮件内容的首部包含一些首部行,每个首部行由key:value组成,其中有些key是必需的,而有些key是可选的,比较中的有:To、Subject等。

To后面可以跟一个或对个收件人的邮件地址,邮件地址格式规定为:用户名@邮箱服务器域名,如sunshuolei@sina.com。

Subject:为可选的关键字,代表了邮件的主题。

From:为必需的关键字,通常由邮件系统自动填入。首部与主题之间用一个空行分割,如下

Date: Wed, 16 Sep2015 20:59:31 +0800 (CST)

From:=?UTF-8?B?5a2Z56GV56OK?= <sunshuolei@sina.com>

To:1325166099@qq.com

Message-ID:<232824863.0.1442408371995.JavaMail.sunshuolei@sina.com>

Subject: Welcome

MIME-Version: 1.0

Content-Type:text/plain; charset=us-ascii

Content-Transfer-Encoding:7bit

 

Hi,shuolei!

.

 

MIME

由于SMTP只能传送一定长度的ASCII码,许多国家的文字(如中文、俄文等)就无法传送,并且也无法传送二进制文件(如图片、音频等);因此提出了多用途网络邮件扩充(MultipurposeInternet Mail Extension,MIME)。

MIME并没有改动SMTP或取代它,MIME继续使用当前的邮件格式,但增加了邮件主体的结构,并定义了传送非ASCII码的编码规则;也就是说MIME仍在现有的邮件协议下工作,MIME与SMTP的关系如下


附件为图片的邮件内容如下:

Date: Wed, 16 Sep 2015 21:33:58 +0800 (CST)

From: =?UTF-8?B?5a2Z56GV56OK?= <sunshuolei@sina.com>

To: 1325166099@qq.com

Message-ID: <225493257.1.1442410438032.JavaMail.sunshuolei@sina.com>

Subject: photo

MIME-Version: 1.0

Content-Type: multipart/mixed;

   boundary="----=_Part_0_2080166188.1442410438012"

 

------=_Part_0_2080166188.1442410438012

Content-Type: text/html;charset=UTF-8

Content-Transfer-Encoding: 7bit

 

this a photo

------=_Part_0_2080166188.1442410438012

Content-Type: image/jpeg; name=photo.jpg

Content-Transfer-Encoding: base64

Content-Disposition: attachment; filename=photo.jpg

 

/9j/4S/+RXhpZgAATU0AKgAAAAgABgESAAMAAAABAAEAAAEaAAUAAAABAAAAVgEbAAUAAAABAAAA

XgEoAAMAAAABAAIAAAITAAMAAAABAAEAAIdpAAQAAAABAAAAZgAAAMAAAABIAAAAAQAAAEgAAAAB

AAeQAAAHAAAABDAyMjGRAQAHAAAABAECAwCgAAAHAAAABDAxMDCgAQADAAAAAQABAACgAgAEAAAA

AQAAAdKgAwAEAAAAAQAAArykBgADAAAAAQAAAAAAAAAAAAYBAwADAAAAAQAGAAABGgAFAAAAAQAA

AQ4BGwAFAAAAAQAAARYBKAADAAAAAQACAAACAQAEAAAAAQAAAR4CAgAEAAAAAQAAIioAAAAAAAAA

SAAAAAEAAABIAAAAAf/Y/9sAQwACAQECAQECAgECAgICAgMFAwMDAwMGBAQDBQcGBwcHBgYGBwgL

CQcICggGBgkNCQoLCwwMDAcJDQ4NDA4LDAwL/9sAQwECAgIDAgMFAwMFCwgGCAsLCwsLCwsLCwsL

/***省略若干***/

cy80G4fI8W/f+8qNX3gpUEf3KomR/MPNAGlsH/POWioPtkv+x+VFBzn/2Q==

------=_Part_0_2080166188.1442410438012--

.

编码方式

BASE64

Base64是一种基于64个可打印字符来表示二进制数据的方法。由于2的6次方等于64,所以每6个bit为一个单元,对应某个可打印字符。三个字节有24个bit,对应于4个Base64单元,即在发送端3个字节可以用4个可打印字符表示,在接收端将4个可打印字符还原为3个字节,依此来在SMTP协议上传输二进制数据。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 、+、/,如下表

 

 

在MIME格式的电子邮件中,base64编码可以用来将二进制的字节序列数据编码成ASCII字符序列构成的文本。base64使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,而等号“=”用来作为后缀用途。

完整的base64定义可见 RFC 1421和 RFC 2045。编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。

将二进制数据转换为文本数据时,将三个字节的二进制数据,放入一个24bit的缓冲区中,先来的字节占高位。若数据不足3个字节,缓冲区中剩下的bit用0填充。然后,每次取出6个bit,以6个bit对应的十进制数作为索引选择对应的文本字符,将选中的字符作为编码后的输出。

重上述过程,直到全部输入数据转换完成。如果最后剩下两个输入数据,在编码结果后加1个“=”;如果最后剩下一个输入数据,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。

 

如果待编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行base64的编码。在编码后的base64文本后加上一个或两个'='号,代表补足的字节数。也就是说,当最后剩余一个八位字节(一个byte)时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。参考下表:

文本(1 Byte)

A

二进制位

0

1

0

0

0

0

0

1

二进制位(补0)

0

1

0

0

0

0

0

1

0

0

0

0

Base64编码

Q

Q

文本(2 Byte)

B

C

二进制位

0

1

0

0

0

0

1

0

0

1

0

0

0

0

1

1

x

x

x

x

x

x

二进制位(补0)

0

1

0

0

0

0

1

0

0

1

0

0

0

0

1

1

0

0

x

x

x

x

x

x

Base64编码

Q

k

M

 

Base64编码可用于在HTTP环境下传递较长的标识信息。例如,在Java持久化系统Hibernate中,就采用了Base64来将一个较长的唯一标识符(一般为128-bitUUID)编码为一个字符串,用作HTTP表单和HTTP GETURL中的参数。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到。

然而,标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。

为解决此问题,可采用一种用于URL的改进Base64编码,它不在末尾填充'='号,并将标准Base64中的“+”和“/”分别改成了“-”和“_”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

另有一种用于正则表达式的改进Base64变种,它将“+”和“/”改成了“!”和“-”,因为“+”,“*”以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。

此外还有一些变种,它们将“+/”改为“_-”或“._”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“_:”(用于XML中的Name)。

Quoted-printable

Quoted-printable, 或QP encoding, 可译为“可打印字符引用编码”、“使用可打印字符的编码”等。Quoted-printable是使用可打印的ASCII字符 (如字母、数字与"=")表示各种编码格式下的字符,以便能在7-bit数据通路上传输8-bit数据, 或者更一般地说在非8-bit 媒体上正确处理数据.这被定义为MIMEcontent transfer encoding,用于 e-mail。

MIME定义了在e-mail中发送各种信息的方法, 包括非英语的其它语言文本信息,使用非ASCII的其它字符编码. 这些编码常常使用ASCII范围以外的值来编码字符,因此需要进一步被编码以便适用于non-8-bit-clean环境. Quoted-printable编码就是把任意字节序列映射为ASCII字符序列.Quoted-printable自身并不是一种字符编码方案, 而是一种在面向字节的编码时的数据编码布置(datacoding layer),即由编码的字符序列如何表示为字节流. QP是可逆的,即可以由原来的非ASCII字符流与QP编码后的字节流来回转换而不失信息。

Quoted-printable与Base64是两种基本的MIME内容传输编码, 如果通常的"8bit"编码不适用。如果文本不含很多非ASCII字符,quoted-printable编码的结果的可读性好,而且紧凑.但是,如果输入的大多数是非ASCII字符,那么quoted-printable编码将变得既不可读又非常低效

任何8-bit字节值可编码为3个字符:一个等号"="后跟随两个十六进制数字(0–9或A–F)表示该字节的数值. 例如,ASCII码换页符 (十进制值为12)可以表示为"=0C",等号"="(十进制值为61)必须表示为"=3D".除了可打印ASCII字符与换行符以外,所有字符必须表示为这种格式.

所有可打印ASCII字符(十进制值的范围为33126)可用ASCII字符编码来直接表示,但是等号"="(十进制值为61)不可以这样直接表示.

ASCII的水平制表符(tab)与空格符, 十进制为9和32, 如果不出现在行尾则可以用其ASCII字符编码直接表示。如果这两个字符出现在行尾,必须QP编码表示为"=09"(tab)或"=20" (space).

如果数据中包含有意义的行结束标志,必须转换为ASCII回车(CR)换行(LF)序列,既不能用原来的ASCII字符也不能用QP编码的"="转义字符序列。相反,如果字节值13与10有其它的不是行结束的含义,它们必须QP编码为=0D与=0A.

quoted-printable编码的数据的每行长度不能超过76个字符. 为满足此要求又不改变被编码文本,在QP编码结果的每行末尾加上软换行(soft linebreak). 即在每行末尾加上一个"=", 但并不会出现在解码得到的文本中. 这种软换行也适用于文本的行非常长,超过了软件限制(例如,某些SMTP软件要求最大行长为1000个字符),这也是RFC 2821允许的.

 

 

附件邮件组织格式

带附件的邮件叫做附件型邮件,附件型附件的格式如下:

 

邮件组织结构关系如下图所示

 

其中

l  MimeMessage表示整封邮件;

l  MimeMultipart表示一个由多个MIME消息组成的组合MIME消息;

l  MimeBodyPart表示邮件的一个MIME消息;

SMTP

简单邮件传输协议(SMTP)是一种基于TCP的电子邮件传输协议,端口为25,控制两个相互通信的SMTP进程交换信息。由于SMTP使用客户-服务器模式,因此负责发送邮件的SMTP进程就是SMTP客户,而负责接收邮件的SMTP进程就是SMTP服务器。SMTP通信有以下过程:

220 smtp-5-123.smtpsmail.fmail.xd.sinanode.com ESMTP

 

EHLO 192.168.1.102

250-smtp-5-123.smtpsmail.fmail.xd.sinanode.com

250-AUTH LOGIN PLAIN

250-AUTH=LOGIN PLAIN

250-STARTTLS

250 8BITMIME

MAIL FROM:<sunshuolei@sina.com>

250 ok

RCPT TO:<1325166099@qq.com>

250 ok

DATA

354 End data with <CR><LF>.<CR><LF>

Date: Thu, 17 Sep 2015 00:08:14 +0800 (CST)

From: =?UTF-8?B?5a2Z56GV56OK?= <sunshuolei@sina.com>

To: 1325166099@qq.com

Message-ID: <232824863.0.1442419694349.JavaMail.sunshuolei@sina.com>

Subject: Welcome

MIME-Version: 1.0

Content-Type: text/plain; charset=us-ascii

Content-Transfer-Encoding: 7bit

 

Hi,sunshuo!

.

250 ok queue id 9542682621961

QUIT

221 smtp-5-123.smtpsmail.fmail.xd.sinanode.com

 

1)       连接建立

发件人的将邮件发送到发送方的邮件服务器缓存后,SMTP客户就每隔一段时间对邮件缓存扫描一次。如果发现有邮件,就与接收方的SMTP服务器建立TCP连接。在连接建立后,接收方SMTP服务器发出”200Service ready”,表示服务就绪。之后,SMTP客户向SMTP服务器发送HELO命名,附上发送的主机名。

SMTP不使用中间的邮件服务器,TCP连接总是在发送发和接收方这个两个服务器之间直接建立,而不管它们相隔多远。

2)       邮件传输

当连接建立后,就要开始传输邮件。邮件的传送从MAIL命令开始,MAIL后面有发件人的地址,如MAIL FROM:<xxx@sina.com>。若SMTP服务器已准备好接收邮件,则回答”250 OK”。接着SMTP客户端发送一个或多个RCPT(收件人recipient的缩写)命令,格式为RCPTTO:<收件人地址>。每发送一个RCPT命令,都应当有相应的信息从SMTP服务器返回,如”200 OK”或”550 Nosuch user here”。

RCPT命令的作用是,先确定接收方是否已做好接收邮件的准备,然后才发送邮件。SMTP客户获得”200 OK”后,客户端就是用DATA命令,表示要开始传输邮件的内容了。正常情况下,SMTP返回的信息是”354 Startmail input:end with<CRLF>.<CRLF>”。<CRLF>是回车换行符。此时SMTP客户端就可以开始传送邮件内容了,并用<CRLF>.<CRLF>表示邮件内容的结束。

3)       连接释放

邮件发送完毕后,SMTP客户发送QUIT命令。SMTP服务器返回”221”(服务关闭),表示SMTP同意释放连接。

 

POP3

 

邮件发送完毕后,SMTP客户发送QUIT命令。SMTP服务器返回”221”(服务关闭),表示SMTP同意释放连接。

邮局协议(Post OfficeProtocol 3,POP3)是一个非常简单的协议,采用“拉”的通信方式。POP也是是用客户-服务器工作方式。另外一个邮件接收协议为网际报文存取协议IMAP,比POP3复杂的多。

此外,随着万维网的流行,目前出现了很多基于万维网的电子邮件,如Hotmail、Gmail等。这种电子邮件的特点是用户浏览器与Hotmail或Gmail的邮件服务器之间发送和接收邮件使用的是HTTP协议,而在不同的邮件服务器之间传送邮件仍然使用SMTP协议。

 

JavaMailAPI

JavaMail是Sun发布的用来出来电子邮件的API,支持各种电子邮件协议,如SMTP、POP3、IMAP等。

JavaMail API的核心类主要包括Session、Message、Address、Authenticator、Transport、Store、Folder。

Session

与Http会话一样,Session类定义了基本的邮件会话。邮件的收发工作都是基于这个会话进行的。Session对象采用java.util.Properties对象获取邮件服务器、用户名、密码等信息。

Message

Message对象用来存储实际发送的电子邮件信息,Message被封装为一个MimeMessage对象来创建,在创建时需要指定一个Session对象做参数。

Address

邮件地址为Address对象,Address也是一个抽象类,可通过InternetAddress来创建Address类。

在创建接收方地址时,往往需要指定该接收者是收件人、抄送人、密送人。假设你要给A发封电子邮件,你要把他的邮箱地址写在收件人一栏,但如果你还想让B收到这封邮件的话,你就可以在抄送一栏里写上B的地址,这样,B不但可以收到这封邮件,而且可以知道你也给A发过一封同样的邮件。如果你在密送一栏里写上B的地址,B仍旧可以收到邮件,但是他不会知道你给A也发了一封同样的邮件。

Authenticator

 

JavaMail API通过使用授权者类以用户名和密码的方式访问邮件服务器。Authenticator为抽象类,在使用时必须采用继承的方式,该继承类必须具有返回PasswordAuthentication对象的getPasswordAutentication()方法。在创建Session对象时,必须注册该继承类的实例。

 

Transport

Transport类根据指定的邮件发送协议(如SMTP),通过指定的账号和密码连接指定的邮件服务器发送邮件。

Store

Store代表了存储邮件的邮件服务器,可从Session中获取指定类型的Store,然后根据指定的账号、密码及授权连接到Store。Store类可以实现特定邮件协议的读、写、监视、查找等操作。

 

Folder

Folder类代表了邮件夹,通过Folder来访问邮件夹中的邮件。对于POP3协议只有一个名为INBOX的Folder有效;而对于IMAP协议,可以访问多个Folder。

 

 

实例

下面是一个简单的发送HTML格式邮件的案例,一个简单的Javamail帮库库参见https://github.com/sanyeshi/simplemail;由于本人水平有限,只作为参考学习之用,欢迎批评指导。

public class JavaMail

{

   public void send() throws Exception

   {

      String smtphost="smtp.sina.com";

      String user="xxx@sina.com";

      String pwd="yyy";

      String from="xxx@sina.com";

      String fromAlias="三叶石";

      String to="zzz@qq.com";

      String subject="问候";

      String content="你好,<b>三叶石</b>";

      Properties prop = new Properties();

      //指定SMTP服务器

      prop.setProperty("mail.smtp.host",smtphost);

      //指定是否需要SMTP验证

      prop.setProperty("mail.smtp.auth","true");

     

      SmtpAuth auth = new SmtpAuth(user,pwd);

      // session

      Session session = Session.getDefaultInstance(prop,auth);

      session.setDebug(true);

      // message

      Message message =new MimeMessage(session);

      message.setSentDate(new Date());

      message.setSubject(subject);

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

      message.setFrom(new InternetAddress(from,fromAlias));

      message.setContent(content,"text/html;charset=utf-8");

      // transport

      Transport transport = session.getTransport("smtp");

      transport.connect(smtphost,user, pwd);

      transport.sendMessage(message,message.getAllRecipients());

      transport.close();

   }

  

  

   static  class SmtpAuth extends Authenticator

   {

      String username;

      String password;

 

      public SmtpAuth(Stringusername, Stringpassword)

      {

           this.username =username;

           this.password =password;

      }

 

      @Override

      protected PasswordAuthenticationgetPasswordAuthentication()

      {

           return new PasswordAuthentication(username,password);

      }

   }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值