Kevin

虫虫的痕迹!!!
  首页  :: 新随笔  :: 订阅 订阅  :: 管理

Jmail接收邮件及UTF-8解码问题(含VB及C#代码)

Posted on 2011-03-04 11:37  KevinYao  阅读(5371)  评论(1编辑  收藏  举报

Jmail接收到的邮件,如果标题的编码方式是UTF-8的话,直接从Base64解码出来的话中文会出现乱码(英文正常),这就导致用Jmail组件收发邮件时有些邮箱的正常,有些邮箱乱码,不够完善。

例如:

QQ邮箱:

  Subject: =?gbk?B?1rG907T4uL28/reiy80=?=

U-Mail邮箱

   Subject: =?utf-8?B?5oiR5b6X57uZ5oiR?=

 

正是因为前几天帮同学弄Jmail收邮件的时候碰到此类问题,所以从网上收集了一些这方面的一些资源,希望能让大家少走点弯路!

 

Jmail的subject共分3段,=?和?=是结束和结尾标记
第一段:utf-8,原来页面的编码方式
第二段:B是指邮件的编码方式B就是base64
第三段:5oiR5b6X57uZ5oiR标题的内容,但是经过Base64编码了
所以出现了QQ邮箱收邮件正常,而收取U-Mail中的邮件时标题乱码的现象,本质就是U-mail用到的是UTF-8编码
看到了本质要解决Jmail收件标题乱码就很简单了。只要获取subject第3个问号和第4个问号间的内容进行base64解码即可。

 

VB部分:

Option Explicit
Dim i&, Attachment&
Dim att As Object
Dim EmailMsg As Object
Dim atts As Object
Dim JMail As Object
Dim EmailList$, Subject$, EmailID&
Dim X$()
Private Sub Command1_Click()
Dim J#
Set JMail = CreateObject("JMail.POP3")
JMail.Connect
"***@163.com", "***", "pop.163.com", "110" 'JMail.Connect "邮箱名", "密码", "服务器" [,"端口号"]
'
Debug.Print "你有" & JMail.Count & "封邮件" '邮件数量
For i = 1 To JMail.Count
' EmailID = JMail.GetMessageUID(I) '邮件唯一ID标识
Set EmailMsg = JMail.Messages.Item(i) '取得一条邮件信息
'
-----------------------------------------------------------------------------取得附件数量并下载
Set atts = EmailMsg.Attachments '附件集合
Attachment = atts.Count '附件的数量
If Attachment > 0 Then
For J = 0 To Attachment - 1
Set att = atts(J)
If Dir(App.Path & "\" & att.Name) = "" Then 'att.Name附件的名称,如果存在同名文件而不加判断则会出错
att.SaveToFile App.Path & "\" & att.Name
End If
Next J
End If
'------------------------------------------------------------------------------以下为各种参数设置
'
EmailMsg.Charset = "gb2312" '编码方式
'
EmailMsg.ContentTransferEncoding = "base64"'解码方式
'
EmailMsg.Encoding = "base64"
'
EmailMsg.ContentType = "multipart/mixed" '发送邮件时
'
EmailMsg.ContentType = "text/html" '接收邮件时
'
EmailMsg.ISOEncodeHeaders = False'True '功能不清?
'
-----------------------------------------------------------------------------可以取得的各元素
'
MsgBox EmailMsg.Priority '邮件的优先级,1-5,1最高,正常情况为3。
'
MsgBox EmailMsg.From '邮件的发送人的信箱地址
'
MsgBox EmailMsg.FromName '邮件的发送人
'
MsgBox EmailMsg.Date '邮件日期
'
MsgBox EmailMsg.Body '邮件内容
'
MsgBox EmailMsg.Size '邮件大小
'
----------------------------------------------------------------------------
Subject = EmailMsg.Headers.GetHeader("Subject") '邮件标题,可正常解码,但UTF-8格式的标题取不全
X = Split(EmailMsg.Headers.GetHeader("Subject"), "?")
If X(1) = "UTF-8" Then
Subject
= Utf8ToUnicode(StrToBytes(X(3)))
Else
Subject
= Base64Decode(X(3))
End If
' Subject = EmailMsg.Headers.GetHeader("From") '发件人,可解码
'
Subject = EmailMsg.Headers.GetHeader("FromName")
EmailList = EmailList & CStr(i) & "" & Subject & vbCrLf
DoEvents
Next
Text1.Text
= EmailList
End Sub

Base64.bas模块:

Option Explicit
Public Const cstBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
Public arrBase64() As String
Private Declare
Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long
Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, ByVal lpDefaultChar As Long, ByVal lpUsedDefaultChar As Long) As Long
Private Const CP_ACP = 0 ' default to ANSI code page
Private Const CP_UTF8 = 65001 ' default to UTF-8 code page
Public Function Base64Encode(strSource As String) As String '编码
On Error Resume Next
If UBound(arrBase64) = -1 Then
arrBase64
= Split(StrConv(cstBase64, vbUnicode), vbNullChar)
End If
Dim arrB() As Byte, bTmp(2) As Byte, bT As Byte
Dim i As Long, J As Long
arrB
= StrConv(strSource, vbFromUnicode)

J
= UBound(arrB)
For i = 0 To J Step 3
Erase bTmp
bTmp(
0) = arrB(i + 0)
bTmp(
1) = arrB(i + 1)
bTmp(
2) = arrB(i + 2)

bT
= (bTmp(0) And 252) / 4
Base64Encode
= Base64Encode & arrBase64(bT)

bT
= (bTmp(0) And 3) * 16
bT
= bT + bTmp(1) \ 16
Base64Encode
= Base64Encode & arrBase64(bT)

bT
= (bTmp(1) And 15) * 4
bT
= bT + bTmp(2) \ 64
If i + 1 <= J Then
Base64Encode
= Base64Encode & arrBase64(bT)
Else
Base64Encode
= Base64Encode & "="
End If

bT
= bTmp(2) And 63
If i + 2 <= J Then
Base64Encode
= Base64Encode & arrBase64(bT)
Else
Base64Encode
= Base64Encode & "="
End If
Next
End Function
Public Function Base64Decode(strEncoded As String) As String '解码
On Error Resume Next
Dim arrB() As Byte, bTmp(3) As Byte, bT As Long, bRet() As Byte
Dim i As Long, J As Long
arrB
= StrConv(strEncoded, vbFromUnicode)
J
= InStr(strEncoded & "=", "=") - 2
ReDim bRet(J - J \ 4 - 1)
For i = 0 To J Step 4
Erase bTmp
bTmp(
0) = (InStr(cstBase64, Chr(arrB(i))) - 1) And 63
bTmp(
1) = (InStr(cstBase64, Chr(arrB(i + 1))) - 1) And 63
bTmp(
2) = (InStr(cstBase64, Chr(arrB(i + 2))) - 1) And 63
bTmp(
3) = (InStr(cstBase64, Chr(arrB(i + 3))) - 1) And 63
bT
= bTmp(0) * 2 ^ 18 + bTmp(1) * 2 ^ 12 + bTmp(2) * 2 ^ 6 + bTmp(3)
bRet((i
\ 4) * 3) = bT \ 65536
bRet((i
\ 4) * 3 + 1) = (bT And 65280) \ 256
bRet((i
\ 4) * 3 + 2) = bT And 255
Next
Base64Decode
= StrConv(bRet, vbUnicode)
End Function

Function StrToBytes(ByVal Source As String) As Byte()
Dim bB64Str() As Byte
bB64Str
= StrConv(Source, vbFromUnicode)
Dim lB64Len As Long
lB64Len
= InStrB(bB64Str, ChrB$(Asc("="))) - 1
Dim lLenPad As Long
lLenPad
= (4 - lB64Len Mod 4) Mod 4
Dim lLen As Long
lLen
= ((lB64Len + lLenPad) \ 4) * 3
Dim bStr() As Byte
ReDim bStr(lLen - 1)
lLen
= lLen - lLenPad
Dim i As Long
Dim lBuffer As Long
For i = 0 To lB64Len - 1 Step 4
lBuffer
= DeB64CodeA(bB64Str(i + 0)) * &H40000 Or DeB64CodeA(bB64Str(i + 1)) * &H1000& _
Or DeB64CodeA(bB64Str(i + 2)) * &H40& Or DeB64CodeA(bB64Str(i + 3))
bStr((i
\ 4) * 3 + 2) = lBuffer And &HFF&
lBuffer
= lBuffer \ &H100&
bStr((i
\ 4) * 3 + 1) = lBuffer And &HFF&
lBuffer
= lBuffer \ &H100&
bStr((i
\ 4) * 3 + 0) = lBuffer And &HFF&
lBuffer
= lBuffer \ &H100&
Next
ReDim Preserve bStr(lLen - 1)
StrToBytes
= bStr
End Function
Private Function DeB64CodeA(ByVal Char As Byte) As Byte
Select Case Char
Case Asc("A") To Asc("Z"): DeB64CodeA = Char - Asc("A")
Case Asc("a") To Asc("z"): DeB64CodeA = Char - Asc("a") + 26
Case Asc("0") To Asc("9"): DeB64CodeA = Char - Asc("0") + 52
Case Asc("+"): DeB64CodeA = 62
Case Asc("/"): DeB64CodeA = 63
Case Asc("="): DeB64CodeA = 64
End Select
End Function
Function Utf8ToUnicode(ByRef Utf() As Byte) As String
Dim lRet As Long
Dim lLength As Long
Dim lBufferSize As Long
lLength
= UBound(Utf) - LBound(Utf) + 1
If lLength <= 0 Then Exit Function
lBufferSize
= lLength * 2
Utf8ToUnicode
= String$(lBufferSize, Chr(0))
lRet
= MultiByteToWideChar(CP_UTF8, 0, VarPtr(Utf(0)), lLength, StrPtr(Utf8ToUnicode), lBufferSize)
If lRet <> 0 Then
Utf8ToUnicode
= Left(Utf8ToUnicode, lRet)
End If
End Function

 C#部分:

#region base64解码
public static string base64GbkDecode(string data)
{
string decode = "";
byte[] bytes = Convert.FromBase64String(data);
try
{
decode
= Encoding.GetEncoding("gb2312").GetString(bytes);
}
catch (Exception ex1)
{
//return "Error in base64Encode" + ex1.Message;
}
return decode;
}

public static string base64Utf8Decode(string data)
{
string result = "";
try
{
System.Text.UTF8Encoding encoder
= new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decode
= encoder.GetDecoder();
byte[] todecode_byte = Convert.FromBase64String(data);
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
char[] decoded_char = new char[charCount];
utf8Decode.GetChars(todecode_byte,
0, todecode_byte.Length, decoded_char, 0);
result
= new String(decoded_char);
}
catch (Exception e)
{
//return "Error in base64Encode" + e.Message;
}
return result;
}

//base64解码
public static string DecodeStr(string allstr, string code)
{
//形如=?...?=是结束开始的标志
//=?utf-8?B?5rWL6K+V5o6l5pS25pys6YKu5Lu26L+Z5piv5Li76aKY?=
//=?gbk?B?suLK1L3TytXN4rK/08q8/tXiuPbKx9b3zOU=?=
//返回的字符串
string str = "";
if (code == "gbk")
{
str
= base64GbkDecode(allstr);
}
else if (code == "utf-8")
{
str
= base64Utf8Decode(allstr);
}
return str;
}
#endregion

调用:

string subjectStr = popMail.Messages[i].Headers.GetHeader("Subject");
subjectstr
= DecodeStr(subjectallstr.Split('?')[3], “utf-8”);

注意:如果原来页面用的是UTF-8编码,一定要用popMail.Messages[i].Headers.GetHeader(“Subject”)的形式,popMail.Messages[i].Subject得到的将是直接经jmail解码后的乱码