MIME详解

MIME,英文全称为"Multipurpose Internet Mail
Extensions",即多用途互联网邮件扩展,是目前互联网
电子邮件普遍遵循的邮件技术规范。在MIME出现之前,互联网电子邮件主要遵循由RFC
822所制定的标准,电子邮件一般只用来传递基本的ASCII码文本信息,MIME在 RFC
822的基础上对电子邮件规范做了大量的扩展,引入了新的格式规范和编码方式,在MIME的支持下,图像、声音、动画等二进制文件都可方便的通过电子邮件来进行传递,极大地丰富了电子邮件的功能。目前互联网上使用的基本都是遵循MIME规范的电子邮件。

    电子邮件的分析和读取一般都通过专用的邮件软件来实现,比如Outlook、Foxmail,但这种第三方软件无法和开发者自己的系统整合,通过对MIME邮件格式的分析,我们可以在自己的应用程序中实现对MIME邮件所含信息的读取。

1  MIME邮件格式分析

    MIME技术规范的完整内容由RFC
2045-2049定义,包括了信息格式、媒体类型、编码方式等各方面的内容,这里我们只介绍其中的一些关键的格式和规范,通过了解这些格式规范,我们就可以实现以编程的方式从MIME邮件中提取基本的邮件信息。

1.1 域

    MIME邮件的基本信息、格式信息、编码方式等重要内容都记录在邮件内的各种域中,域的基本格式:{域名}:{内容},域由域名后面跟":"再加上域的信息内容构成,一条域在邮件中占一行或者多行,域的首行左侧不能有空白字符,比如空格或者制表符,占用多行的域其后续行则必须以空白字符开头。域的信息内容中还可以包含属性,属性之间以";"分隔,属性的格式如下:{属性名称}="{属性值}"。

    表1是一封示例邮件的内容,其中行1-5、行8都是单行的域,行6-7则是一个多行的域,并带有一个名为charset的属性,属性值为us-ascii。

表1
示例电子邮件




行1 From: "suntao"
<[email protected]>

行2 To:
<[email protected]>

行3 Subject: hello world

行4 Date: Mon, 9 Oct 2006
16:51:34 +0800

行5 MIME-Version: 1.0

行6 Content-Type:
text/plain;

行7           
charset="us-ascii"

行8 Date: Mon, 9 Oct 2006
16:48:25 +0800

行9

行10 Hello world

行11

    邮件规范中定义了大量域,分别用来存储同邮件相关的各种信息,比如发件人的名字和邮件地址信息存储在From域中,收件人的邮件地址信息存储在To域中,开发人员可通过查询RFC文档得到完整的邮件域定义列表。

1.2  Content-Type域

    Content-Type域定义了邮件中所含各种内容的类型以及相关属性。邮件所含的文本、超文本、附件等信息都按照对应Content-Type域所指定的媒体类型、存储位置、编码方式等信息存储在邮件中。Content-Type域基本格式:Content-Type:{主类型}/{子类型}。

示例邮件中的行6-7就是一个Content-Type域,主类型为text,子类型为plain,字符集属性为us-ascii。

表2:MIME邮件中常见的主类型






















主类型

常见属性

参数含义

text

charset

文本信息所使用的字符集

image

name

图像的名称

application

name

应用程序的名称

multipart

boundary

邮件分段边界标识

1.3  multipart类型

    MIME邮件中各种不同类型的内容是分段存储的,各个段的排列方式、位置信息都通过Content-Type域的multipart类型来定义。multipart类型主要有三种子类型:mixed、alternative、related。

1.3.1
 multipart类型基本格式

●  multipart/mixed类型

    如果一封邮件中含有附件,那邮件的Content-Type域中必须定义multipart/mixed类型,邮件通过multipart/mixed类型中定义的boundary标识将附件内容同邮件其它内容分成不同的段。基本格式如下:

Content-Type:
multipart/mixed;

boundary="{分段标识}"

●  multipart/alternative类型

    MIME邮件可以传送超文本内容,但出于兼容性的考虑,一般在发送超文本格式内容的同时会同时发送一个纯文本内容的副本,如果邮件中同时存在纯文本和超文本内容,则邮件需要在Content-Type域中定义multipart/alternative类型,邮件通过其boundary中的分段标识将纯文本、超文本和邮件的其它内容分成不同的段。基本格式如下:

Content-Type:
multipart/alternative;

boundary="{分段标识}"

●  multipart/related类型

    MIME邮件中除了可以携带各种附件外,还可以将其它内容以内嵌资源的方式存储在邮件中。比如我们在发送html格式的邮件内容时,可能使用图像作为html的背景,html文本会被存储在alternative段中,而作为背景的图像则会存储在multipart/related类型定义的段中。基本格式如下:

Content-Type:
multipart/related;

type="multipart/alternative";

boundary="{分段标识}"

1.3.2
 multipart类型的boundary属性

    multipart的子类型中都定义了各自的boundary属性,邮件使用这些boundary中定义的字符串作为标识,将邮件内容分成不同的段,段体内的每个子段以"--"+boundary行开始,父段则以"--"+boundary+"--"行结束,不同段之间用空行分隔。

1.3.3
 multipart类型的层次关系

表3:multipart子类型之间的层次关系








Multipart/mixed









Multipart/related









Multipart/alternative

纯文本正文

超文本正文

内嵌资源

附件

    MIME邮件通过多个Content-Type域的multipart类型将内容分成不同的段,这些段在邮件中不是线形顺序排列的,而是存在一个互相包含的层次关系,multipart子类型之间的层次关系结构如表3。

1.4
 Content-Transfer-Encoding域

    MIME邮件可以传送图像、声音、视频以及附件,这些非ASCII码的数据都是通过一定的编码规则进行转换后附着在邮件中进行传递的。编码方式存储在邮件的Content-Transfer-Encoding域中,一封邮件中可能有多个Content-Transfer-Encoding域,分别对应邮件不同部分内容的编码方式。目前MIME邮件中的数据编码普遍采用Base64编码或Quoted-printable编码来实现。

1.4.1
 Base64编码

    Base64编码的目的是将输入的数据全部转换成由64个指定ASCII字符组成的字符序列,
这64个字符由{‘A‘-‘Z‘, ‘a‘-‘z‘, ‘0‘-‘9‘, ‘+‘,
‘/‘}构成。编码时将需要转换的数据每次取出6bit,然后将其转换成十进制数字,这个数字的范围最小为0,最大为63,然后查询{‘A‘-‘Z‘, ‘a‘-‘z‘,
‘0‘-‘9‘, ‘+‘,
‘/‘}构成的字典表,输出对应位置的ASCII码字符,这样每3个字节的数据内容会被转换成4个字典中的ASCII码字符,当转换到数据末尾不足3个字节时,则用"="来填充。

1.4.2
 Quoted-printable编码

    Quoted-printable编码的目的也是将输入的信息转换成可打印的ASCII码字符,但它是根据信息的内容来决定是否进行编码,如果读入的字节处于33-60、62-126范围内的,这些都是可直接打印的ASCII字符,则直接输出,如果不是,则将该字节分为两个4bit,每个用一个16进制数字来表示,然后在前面加"=",这样每个需要编码的字节会被转换成三个字符来表示。

2  MIME邮件信息提取

    从上面的分析可以看出,MIME邮件传递的实际是一个经过特殊编码并以约定格式排列的字符序列,我们只需要提取存储在邮件各种域中的格式、位置和编码信息,按照根据这些信息从字符序列中提取出对应的字符内容并对其进行反向解码,就可以得到我们需要的有关内容。

下面给出.Net环境下,利用C#结合正则表达式从邮件中提取相关信息的基本思路和部分代码。

2.1 收件人/发件人/邮件主题的提取

    收件人、发件人、邮件主题是一封邮件的基本组成信息,分别存邮件的From域、To域、Subject域中。开发中只需要通过正则表达式来匹配这些指定的域,然后从匹配结果中取出相关信息即可。

    示例代码:提取邮件主题

string
emailContent = "……";//emailContent中存储的是邮件内容

pat
= @"^Subject:\s*(?<title>.*)\s*\r\n";

myMatches
= Regex.Matches(emailContent,pat,RegexOptions.Multiline);

foreach(Match
nextMatch in myMatches)

{

GroupCollection myGroup = nextMatch.Groups;

string title = myGroup["title"].ToString();//title变量存储From域的内容

……

}

    需要注意的是上面的代码提取的是跟随在Subject:后面的字符串,如果邮件的主题内容是中文或者其它需要编码的地区文字,则还需要对其进行解码。比如,如果邮件的Subject域中的信息是"你好",那么提取出来的字符串会是这种形式:=?gb2312?B?xOO6ww==?=,第一个?同第二个?之间的gb2312代表标题内容所使用的字符集,第二个?和第三个?之间的B代表这部分内容采用的是base64编码方式,如果采用Quoted-printabel编码方式则显示Q,第三个?和第四个?之间则是"你好"经过base64编码后的字符串。

2.2
 multipart分段信息的提取

    邮件通过multipart类型将内容分隔成不同的段,各段之间的边界标识由对应multipart类型的boundary属性定义。要从邮件中提取出需要的内容,首先需要提取出邮件中的分段信息。下面的代码从一封邮件中提取出所有的multipart类型的名称和boundary属性。

示例代码:提取multipart信息

string
emailContent = "……";//emailContent中存储的是邮件内容

string
pat =
@"\bContent-Type:\s*(?<type>\w+/\w+);\s+(type=\S(?<subtype>\S+)\S)?\s+boundary=""(?<flag>\S+)""";

MatchCollection
myMatches = Regex.Matches(emailContent,pat);

foreach(Match
nextMatch in myMatches)

{

GroupCollection myGroup = nextMatch.Groups;

string type = myGroup["type"].ToString();//type变量存储multipart类型的名称

string flag = myGroup["flag"].ToString();//flag变量存储multipart类型的boundary属性

……

}

2.3  邮件附件的提取

    邮件中的附件信息由对应的Content-Type域、Content-Transfer-Encoding域、Content-Disposition域和multipart/mixed类型定义,前三个域定义附件的类型、名称和编码方式,multipart/mixed则定义附件同邮件其它内容的分段标识。基本格式如下:




--boundary分段标识

Content-Type:
application/msword;

name="readme.doc"

Content-Transfer-Encoding: base64

Content-Disposition:
attachment;

filename=" readme.doc "

……

文件内容的Base64编码

……

--boundary分段标识

    示例代码:提取邮件附件

//boundaryMixed代表已经提取出的multipart/mixed类型的boundary标识

//DecodeBase64为自定义的base64解码函数

//DecodeQuotedPrintable为自定义的quoted-printable解码函数

string
emailContent = "……";//emailContent中存储的是邮件内容

string
pat =
@"\r\nContent-Type:\s*(?<filetype>\S*);\s*name=""(?<name>\S*)""\s*Content-Transfer-Encoding:\s*(?<encoding>\S*)\s*Content-Disposition:\s*attachment;\s*filename=""(?<filename>\S+)""\s+(?<content>[\S|\r\n]+)"
+ "--" + boundaryMixed;

MatchCollection
myMatches =
Regex.Matches(emailContent,pat,RegexOptions.Singleline);

foreach(Match
nextMatch in myMatches)

{

//提取附件的类型、编码方式、文件名、内容信息

GroupCollection myGroup = nextMatch.Groups;

string fileType = myGroup["filetype"].ToString();

string encoding = myGroup["encoding"].ToString();

string fileName = myGroup["filename"].ToString();

string content = myGroup["content"].ToString().Trim();

byte[] attachFile;

//根据附件的编码方式对提取出的附件内容进行解码

if(encoding == "base64")

{

attachFile = DecodeBase64 (content);

}

if(encoding
== "quoted-printable")

{

attachFile = DecodeQuotedPrintable (content);

}

//将解码后的内容写入磁盘

FileStream fs = new FileStream("c:\\" + fileName,

FileMode.CreateNew);

BinaryWriter bw = new BinaryWriter(fs);

bw.Write(attachFile);

bw.Close();

fs.Close();

}

    上面的程序从邮件原文中提取出附件信息,并根据附件采用的编码类型进行解码,然后将解码后的内容按照原文件名存储到C盘根目录。同样,如果附件的文件名是中文或者其它需要编码的文字,则首先需要对文件名进行解码。

3  总结

    本文对MIME邮件的基本格式做了分析和阐述,介绍了MIME中几个重要的规范和定义,并给出了利用正则表达式从邮件内容中提取相关信息的基本思路和方法。在开发中需要注意的是,邮件中所含的内容决定了邮件的具体格式,multipart类型以及对应的分段标识只有在有相关内容的时候才会在邮件中出现,在开发时需要具体分析。MIME的详细技术规范可以查询RFC的相关文档
MIME详解,布布扣,bubuko.com

时间: 2024-08-05 18:02:19

MIME详解的相关文章

HttpServletResponse和HttpServletRequest详解

HttpServletResponse,HttpServletRequest详解 1.相关的接口 HttpServletRequest HttpServletRequest接口最常用的方法就是获得请求中的参数,这些参数一般是客户端表单中的数据.同时,HttpServletRequest接口可以获取由客户端传送的名称,也可以获取产生请求并且接收请求的服务器端主机名及IP地址,还可以获取客户端正在使用的通信协议等信息.下表是接口HttpServletRequest的常用方法. 说明:HttpServ

jquery ajax 方法及各参数详解

jquery ajax 方法及各参数详解 1.$.ajax() 只有一个参数:参数 key/value 对象,包含各配置及回调函数信息. 参数列表: 参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: "GET") 请求方式 ("POST" 或 "GET"), 默认为 "GET".注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分

高性能Web服务之nginx应用详解

一.Nginx特性 * *模块化,目前只能将模块编译进Nginx,暂时不支持动态装卸模块.(httpd优势) * *可靠性,一个主进程(master)控制多个工作进程(worker),工作进程响应用户多个请求(httpd劣势) * *低内存消耗,(httpd劣势) * *支持热部署,(httpd相同) * *支持事件驱动I/O,AI/O,支持mmap(httpd2.4才算支持event,劣势) 二.Nginx基本架构 Nginx由一个master进程生成多个worker进程,每个worker进程

WebView使用详解(三)——WebChromeClient与LoadData补充

前言: 我不会忘了我 忘了我曾说过一定会得到的梦想 --<老大>小柯 相关文章 1.<WebView使用详解(一)--Native与JS相互调用(附JadX反编译)> 2.<WebView使用详解(二)--WebViewClient与常用事件监听> 一.WebChromeClient 1.概述 (1). 与WebViewClient的区别 很多同学一看到这里有Chrome,立马就会想到google 的Chrome浏览器:这里并不是指Chrome浏览器的意思,而是泛指浏览

MongoDB系列教程(八):GridFS存储详解

MongoDB系列教程(八):GridFS存储详解 GridFS简介 mongoDB的文档以BSON格式存储,支持二进制的数据类型,当我们把二进制格式的数据直接保存到mongoDB的文档中.但是当文件太大时,例如图片和视频等文件,每个文档的长度是有限的,于是mongoDb会提供了一种处理大文件的规范--GridFS. GridFS实现原理 在GridFS数据库中,默认使用fs.chunks 和fs.files来存储文件,其中fs.files集合存放文件的信息,fs.chunks存放文件的数据,一

JQuery中$.ajax()方法参数详解

url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. type: 要求为String类型的参数,请求方式(post或get)默认为get.注意其他http请求方法,例如put和 delete也可以使用,但仅部分浏览器支持. timeout: 要求为Number类型的参数,设置请求超时时间(毫秒).此设置将覆盖$.ajaxSetup()方法的全局设 置. async:要求为Boolean类型的参数,默认设置为true,所有请求均为异步请求. 如果需要发送同步请求,请将此选项

HttpWebRequest中的ContentType详解

1.参考网络资源: http://blog.csdn.net/blueheart20/article/details/45174399  ContentType详解 http://www.tuicool.com/articles/eiauAb   Ajax的请求注意 http://www.cnblogs.com/kissdodog/archive/2013/04/06/3002779.html HttpWebRequest的详解 http://tool.oschina.net/commons 

Nginx反向代理、动静分离、负载均衡及rewrite隐藏路径详解(Nginx Apache MySQL Redis)–第三部分

Nginx反向代理.动静分离.负载均衡及rewrite隐藏路径详解 (Nginx Apache MySQL Redis) 楓城浪子原创,转载请标明出处! 更多技术博文请见个人博客:https://fengchenglangzi.000webhostapp.com 微信bh19890922 QQ445718526.490425557 三.Nginx动静分离及负载均衡 3.1 Nginx安装 请参考:https://fengchenglangzi.000webhostapp.com/?p=511 亦

【转载】HTTP协议详解

[本文转自]http://www.cnblogs.com/EricaMIN1987_IT/p/3837436.html 一.概念 协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则,超文本传输协议(HTTP)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器. HTTP协议,即超文本传输协议(Hypertext transfer protocol).是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相