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盘根目录。同样,如果附件的文件名是中文或者其它需要编码的文字,则首先需要对文件名进行解码。 |