如何使用 Gmail API 检索我的 Gmail 邮件?
How to retrieve my Gmail messages using Gmail API?
我想达到的目标:
我正在使用 Gmail API 并且基本上我想连接到我的 GMail 帐户以阅读我的收件箱类别的电子邮件,并获取基本信息每条消息(title/subject、from、to、date 和发件人)。
问题:
我正在尝试根据自己的需要调整用 C# 编写的 this Google 示例,我正在寻找解决方案C# 或 Vb.Net,无所谓。
(请注意,Google 为不同的用户国家显示了不同的代码示例,因此该网页的代码可能不会对每个人都相同,Google 的逻辑确实如此糟透了。)
我在下面的代码中遇到的问题是:
- 我在
lblInbox.MessagesTotal
属性 中得到一个空值。
msgItem.Raw
属性 也总是空的。
- 我还没有发现如何只解析收件箱类别中的邮件。
- 我还没有发现如何确定消息是已读还是未读。
- 我还没有发现如何确定邮件的基本信息(主题、发件人、收件人、日期、发件人)。
这是我试过的方法,请注意,在改编 Google 的示例时,我假设 "user"
参数应该是 Gmail 用户帐户名 ("MyEmail@GMail.com"
) ,但我不确定应该是这样。
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Services
Imports Google.Apis.Util.Store
Imports Google.Apis.Gmail
Imports Google.Apis.Gmail.v1
Imports Google.Apis.Gmail.v1.Data
Imports Google.Apis.Gmail.v1.UsersResource
Public Class Form1 : Inherits Form
Private Async Sub Test() Handles MyBase.Shown
Await GmailTest()
End Sub
Public Async Function GmailTest() As Task
Dim credential As UserCredential
Using stream As New FileStream("C:\GoogleAPIKey.json", FileMode.Open, FileAccess.Read)
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
{GmailService.Scope.MailGoogleCom},
"MyEmail@GMail.com",
CancellationToken.None)
End Using
' Create the service.
Dim service As New GmailService(New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "What I need to put here?"
})
' Get the "INBOX" label/category.
Dim lblReq As UsersResource.LabelsResource.ListRequest = service.Users.Labels.List("me")
Dim lblInbox As Data.Label = lblReq.Execute().Labels.Where(Function(lbl) lbl.Name = "INBOX").Single
Dim msgCount As Integer? = lblInbox.MessagesTotal
MsgBox("Messages Count: " & msgCount)
If (msgCount <> 0) Then
' Define message parameters of request.
Dim msgReq As UsersResource.MessagesResource.ListRequest = service.Users.Messages.List("me")
' List messages of INBOX category.
Dim messages As IList(Of Data.Message) = msgReq.Execute().Messages
Console.WriteLine("Messages:")
If (messages IsNot Nothing) AndAlso (messages.Count > 0) Then
For Each msgItem As Data.Message In messages
MsgBox(msgItem.Raw)
Next
End If
End If
End Function
End Class
问题:
我会提出最重要的需求(但是,非常欢迎任何帮助解决其他提到的问题):
- 在 C# 或 VB.Net 中,如何获取一个集合来迭代 所有 INBOX 组中的电子邮件?
更新:
这是我现在正在使用的代码,目的是检索指定邮箱标签的所有Message
的集合,问题是 newMsg
对象的 Payload
和 Body
成员为空,所以我无法阅读电子邮件。
我做错了什么?
Public Async Function GetMessages(ByVal folder As Global.Google.Apis.Gmail.v1.Data.Label) As Task(Of List(Of Global.Google.Apis.Gmail.v1.Data.Message))
If Not (Me.isAuthorizedB) Then
Throw New InvalidOperationException(Me.authExceptionMessage)
Else
Dim msgsRequest As UsersResource.MessagesResource.ListRequest = Me.client.Users.Messages.List("me")
With msgsRequest
.LabelIds = New Repeatable(Of String)({folder.Id})
.MaxResults = 50
'.Key = "YOUR API KEY"
End With
Dim msgsResponse As ListMessagesResponse = Await msgsRequest.ExecuteAsync()
Dim messages As New List(Of Global.Google.Apis.Gmail.v1.Data.Message)
Do While True
For Each msg As Global.Google.Apis.Gmail.v1.Data.Message In msgsResponse.Messages
Dim msgRequest As UsersResource.MessagesResource.GetRequest = Me.client.Users.Messages.Get("me", msg.Id)
msgRequest.Format = MessagesResource.GetRequest.FormatEnum.Full
Dim newMsg As Message = Await msgRequest.ExecuteAsync()
messages.Add(newMsg)
Next msg
If Not String.IsNullOrEmpty(msgsResponse.NextPageToken) Then
msgsRequest.PageToken = msgsResponse.NextPageToken
msgsResponse = Await msgsRequest.ExecuteAsync()
Else
Exit Do
End If
Loop
Return messages
End If
End Function
GoogleWebAuthorizationBroker.AuthorizeAsync 中的用户参数仅供 FileDatastore 用于存储您的凭据,请查看我的教程 Google .net – FileDatastore demystified 了解更多信息。
我的 VB.net 已经生锈了 6 年了,但是在 C# 中你可以这样做
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List("Users email address");
var response = request.Execute();
foreach (var item in response.Messages) {
Console.WriteLine(item.Payload.Headers);
}
MessageResource.ListRequest returns 消息列表 objects 您可以循环浏览它们。
Users.Messages 包含 header 应该有主题和来往。
我还有一个关于 gmail 的非常古老的 C# 教程可能会有所帮助。
更新以回答您的更新:
删除后会发生什么:
.LabelIds = New Repeatable(Of String)({folder.Id})
labelIds string Only return messages with labels that match all of the specified label IDs.
您发送的似乎是文件夹 ID。尝试使用 user.lables.list 其中 returns 列出用户邮箱中的所有标签
目前由于某种原因,许多属性正在从任何请求中返回 null
。如果我们有电子邮件 ID 列表,我们仍然可以解决这个问题。然后我们可以使用这些电子邮件 ID 发送另一个请求以检索更多详细信息:from
、date
、subject
和 body
。 @DalmTo 也在正确的轨道上,但与 headers 还不够接近,因为它最近发生了变化,这将需要更多的请求。
private async Task getEmails()
{
try
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows for read-only access to the authenticated
// user's account, but not other types of account access.
new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.MailGoogleCom, GmailService.Scope.GmailModify },
"NAME OF ACCOUNT NOT EMAIL ADDRESS",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var gmailService = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var emailListRequest = gmailService.Users.Messages.List("EMAILADDRESSHERE");
emailListRequest.LabelIds = "INBOX";
emailListRequest.IncludeSpamTrash = false;
//emailListRequest.Q = "is:unread"; // This was added because I only wanted unread emails...
// Get our emails
var emailListResponse = await emailListRequest.ExecuteAsync();
if (emailListResponse != null && emailListResponse.Messages != null)
{
// Loop through each email and get what fields you want...
foreach (var email in emailListResponse.Messages)
{
var emailInfoRequest = gmailService.Users.Messages.Get("EMAIL ADDRESS HERE", email.Id);
// Make another request for that email id...
var emailInfoResponse = await emailInfoRequest.ExecuteAsync();
if (emailInfoResponse != null)
{
String from = "";
String date = "";
String subject = "";
String body = "";
// Loop through the headers and get the fields we need...
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
{
date = mParts.Value;
}
else if(mParts.Name == "From" )
{
from = mParts.Value;
}
else if (mParts.Name == "Subject")
{
subject = mParts.Value;
}
if (date != "" && from != "")
{
if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
{
body = emailInfoResponse.Payload.Body.Data;
}
else
{
body = getNestedParts(emailInfoResponse.Payload.Parts, "");
}
// Need to replace some characters as the data for the email's body is base64
String codedBody = body.Replace("-", "+");
codedBody = codedBody.Replace("_", "/");
byte[] data = Convert.FromBase64String(codedBody);
body = Encoding.UTF8.GetString(data);
// Now you have the data you want...
}
}
}
}
}
}
catch (Exception)
{
MessageBox.Show("Failed to get messages!", "Failed Messages!", MessageBoxButtons.OK);
}
}
static String getNestedParts(IList<MessagePart> part, string curr)
{
string str = curr;
if (part == null)
{
return str;
}
else
{
foreach (var parts in part)
{
if (parts.Parts == null)
{
if (parts.Body != null && parts.Body.Data != null)
{
str += parts.Body.Data;
}
}
else
{
return getNestedParts(parts.Parts, str);
}
}
return str;
}
}
目前,此方法将检索所有电子邮件 ID,并为每个电子邮件 ID 获取每封电子邮件的 subject
、from
、date
和 body
。整个方法都有注释。如果有什么不明白的地方,请告诉我。另一个注意事项:在将其作为答案发布之前再次进行了测试。
抱歉,这不是一个答案,我无法在 Zaggler 的答案中添加评论(刚刚加入),所以只是 post 作为一个新答案,Zaggler 的答案很好,但是有一个小问题问题。当电子邮件 body 包含多个部分时。 Convert.FromBase64..... 不适用于两个连接的 base64 字符串。所以会发生异常。更好地转换然后加入 body 部分。
有人要代码,这里是完整的测试代码。其中大部分是从 Zaggler 复制的,但我最终有一些例外。所以我追查到上述问题。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
namespace GmailTests
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-dotnet-quickstart.json
static string[] Scopes = { GmailService.Scope.GmailModify };
static string ApplicationName = "Gmail API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/gmail-dotnet-quickstart2.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Gmail API service.
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var re = service.Users.Messages.List("me");
re.LabelIds = "INBOX";
re.Q = "is:unread"; //only get unread;
var res = re.Execute();
if (res != null && res.Messages != null)
{
Console.WriteLine("there are {0} emails. press any key to continue!", res.Messages.Count);
Console.ReadKey();
foreach (var email in res.Messages)
{
var emailInfoReq = service.Users.Messages.Get("me", email.Id);
var emailInfoResponse = emailInfoReq.Execute();
if (emailInfoResponse != null)
{
String from = "";
String date = "";
String subject = "";
String body = "";
//loop through the headers and get the fields we need...
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
{
date = mParts.Value;
}
else if (mParts.Name == "From")
{
from = mParts.Value;
}
else if (mParts.Name == "Subject")
{
subject = mParts.Value;
}
if (date != "" && from != "")
{
if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
body = DecodeBase64String(emailInfoResponse.Payload.Body.Data);
else
body = GetNestedBodyParts(emailInfoResponse.Payload.Parts, "");
//now you have the data you want....
}
}
//Console.Write(body);
Console.WriteLine("{0} -- {1} -- {2}", subject, date, email.Id);
Console.ReadKey();
}
}
}
}
static String DecodeBase64String(string s)
{
var ts = s.Replace("-", "+");
ts = ts.Replace("_", "/");
var bc = Convert.FromBase64String(ts);
var tts = Encoding.UTF8.GetString(bc);
return tts;
}
static String GetNestedBodyParts(IList<MessagePart> part, string curr)
{
string str = curr;
if (part == null)
{
return str;
}
else
{
foreach (var parts in part)
{
if (parts.Parts == null)
{
if (parts.Body != null && parts.Body.Data != null)
{
var ts = DecodeBase64String(parts.Body.Data);
str += ts;
}
}
else
{
return GetNestedBodyParts(parts.Parts, str);
}
}
return str;
}
}
}
}
首先:对@codexer 的回答投赞成票。
其次,在他的代码中使用以下函数来解码base64URL编码的主体。 Google 不仅对正文进行了 base64 编码,它还进行了 URL 编码 :-/
/// <summary>
/// Turn a URL encoded base64 encoded string into readable UTF-8 string.
/// </summary>
/// <param name="sInput">base64 URL ENCODED string.</param>
/// <returns>UTF-8 formatted string</returns>
private string DecodeURLEncodedBase64EncodedString(string sInput)
{
string sBase46codedBody = sInput.Replace("-", "+").Replace("_", "/").Replace("=", String.Empty); //get rid of URL encoding, and pull any current padding off.
string sPaddedBase46codedBody = sBase46codedBody.PadRight(sBase46codedBody.Length + (4 - sBase46codedBody.Length % 4) % 4, '='); //re-pad the string so it is correct length.
byte[] data = Convert.FromBase64String(sPaddedBase46codedBody);
return Encoding.UTF8.GetString(data);
}
UsersResource.MessagesResource.GetRequest getReq = null;
Google.Apis.Gmail.v1.Data.Message msg = null;
getReq = gmailServiceObj.Users.Messages.Get(userEmail, MessageID);
getReq.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
msg = getReq.Execute();
string converted = msg.Raw.Replace('-', '+');
converted = converted.Replace('_', '/');
byte[] decodedByte = Convert.FromBase64String(converted);
converted = null;
f_Path = Path.Combine(m_CloudParmsObj.m_strDestinationPath,MessageID + ".eml");
if (!Directory.Exists(m_CloudParmsObj.m_strDestinationPath))
Directory.CreateDirectory(m_CloudParmsObj.m_strDestinationPath);
// Create eml file
File.WriteAllBytes(f_Path, decodedByte);
我们可以得到包含所有消息属性的 .eml 文件。
我想达到的目标:
我正在使用 Gmail API 并且基本上我想连接到我的 GMail 帐户以阅读我的收件箱类别的电子邮件,并获取基本信息每条消息(title/subject、from、to、date 和发件人)。
问题:
我正在尝试根据自己的需要调整用 C# 编写的 this Google 示例,我正在寻找解决方案C# 或 Vb.Net,无所谓。
(请注意,Google 为不同的用户国家显示了不同的代码示例,因此该网页的代码可能不会对每个人都相同,Google 的逻辑确实如此糟透了。)
我在下面的代码中遇到的问题是:
- 我在
lblInbox.MessagesTotal
属性 中得到一个空值。 msgItem.Raw
属性 也总是空的。- 我还没有发现如何只解析收件箱类别中的邮件。
- 我还没有发现如何确定消息是已读还是未读。
- 我还没有发现如何确定邮件的基本信息(主题、发件人、收件人、日期、发件人)。
这是我试过的方法,请注意,在改编 Google 的示例时,我假设 "user"
参数应该是 Gmail 用户帐户名 ("MyEmail@GMail.com"
) ,但我不确定应该是这样。
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Services
Imports Google.Apis.Util.Store
Imports Google.Apis.Gmail
Imports Google.Apis.Gmail.v1
Imports Google.Apis.Gmail.v1.Data
Imports Google.Apis.Gmail.v1.UsersResource
Public Class Form1 : Inherits Form
Private Async Sub Test() Handles MyBase.Shown
Await GmailTest()
End Sub
Public Async Function GmailTest() As Task
Dim credential As UserCredential
Using stream As New FileStream("C:\GoogleAPIKey.json", FileMode.Open, FileAccess.Read)
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
{GmailService.Scope.MailGoogleCom},
"MyEmail@GMail.com",
CancellationToken.None)
End Using
' Create the service.
Dim service As New GmailService(New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "What I need to put here?"
})
' Get the "INBOX" label/category.
Dim lblReq As UsersResource.LabelsResource.ListRequest = service.Users.Labels.List("me")
Dim lblInbox As Data.Label = lblReq.Execute().Labels.Where(Function(lbl) lbl.Name = "INBOX").Single
Dim msgCount As Integer? = lblInbox.MessagesTotal
MsgBox("Messages Count: " & msgCount)
If (msgCount <> 0) Then
' Define message parameters of request.
Dim msgReq As UsersResource.MessagesResource.ListRequest = service.Users.Messages.List("me")
' List messages of INBOX category.
Dim messages As IList(Of Data.Message) = msgReq.Execute().Messages
Console.WriteLine("Messages:")
If (messages IsNot Nothing) AndAlso (messages.Count > 0) Then
For Each msgItem As Data.Message In messages
MsgBox(msgItem.Raw)
Next
End If
End If
End Function
End Class
问题:
我会提出最重要的需求(但是,非常欢迎任何帮助解决其他提到的问题):
- 在 C# 或 VB.Net 中,如何获取一个集合来迭代 所有 INBOX 组中的电子邮件?
更新:
这是我现在正在使用的代码,目的是检索指定邮箱标签的所有Message
的集合,问题是 newMsg
对象的 Payload
和 Body
成员为空,所以我无法阅读电子邮件。
我做错了什么?
Public Async Function GetMessages(ByVal folder As Global.Google.Apis.Gmail.v1.Data.Label) As Task(Of List(Of Global.Google.Apis.Gmail.v1.Data.Message))
If Not (Me.isAuthorizedB) Then
Throw New InvalidOperationException(Me.authExceptionMessage)
Else
Dim msgsRequest As UsersResource.MessagesResource.ListRequest = Me.client.Users.Messages.List("me")
With msgsRequest
.LabelIds = New Repeatable(Of String)({folder.Id})
.MaxResults = 50
'.Key = "YOUR API KEY"
End With
Dim msgsResponse As ListMessagesResponse = Await msgsRequest.ExecuteAsync()
Dim messages As New List(Of Global.Google.Apis.Gmail.v1.Data.Message)
Do While True
For Each msg As Global.Google.Apis.Gmail.v1.Data.Message In msgsResponse.Messages
Dim msgRequest As UsersResource.MessagesResource.GetRequest = Me.client.Users.Messages.Get("me", msg.Id)
msgRequest.Format = MessagesResource.GetRequest.FormatEnum.Full
Dim newMsg As Message = Await msgRequest.ExecuteAsync()
messages.Add(newMsg)
Next msg
If Not String.IsNullOrEmpty(msgsResponse.NextPageToken) Then
msgsRequest.PageToken = msgsResponse.NextPageToken
msgsResponse = Await msgsRequest.ExecuteAsync()
Else
Exit Do
End If
Loop
Return messages
End If
End Function
GoogleWebAuthorizationBroker.AuthorizeAsync 中的用户参数仅供 FileDatastore 用于存储您的凭据,请查看我的教程 Google .net – FileDatastore demystified 了解更多信息。
我的 VB.net 已经生锈了 6 年了,但是在 C# 中你可以这样做
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List("Users email address");
var response = request.Execute();
foreach (var item in response.Messages) {
Console.WriteLine(item.Payload.Headers);
}
MessageResource.ListRequest returns 消息列表 objects 您可以循环浏览它们。
Users.Messages 包含 header 应该有主题和来往。
我还有一个关于 gmail 的非常古老的 C# 教程可能会有所帮助。
更新以回答您的更新:
删除后会发生什么:
.LabelIds = New Repeatable(Of String)({folder.Id})
labelIds string Only return messages with labels that match all of the specified label IDs.
您发送的似乎是文件夹 ID。尝试使用 user.lables.list 其中 returns 列出用户邮箱中的所有标签
目前由于某种原因,许多属性正在从任何请求中返回 null
。如果我们有电子邮件 ID 列表,我们仍然可以解决这个问题。然后我们可以使用这些电子邮件 ID 发送另一个请求以检索更多详细信息:from
、date
、subject
和 body
。 @DalmTo 也在正确的轨道上,但与 headers 还不够接近,因为它最近发生了变化,这将需要更多的请求。
private async Task getEmails()
{
try
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows for read-only access to the authenticated
// user's account, but not other types of account access.
new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.MailGoogleCom, GmailService.Scope.GmailModify },
"NAME OF ACCOUNT NOT EMAIL ADDRESS",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var gmailService = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var emailListRequest = gmailService.Users.Messages.List("EMAILADDRESSHERE");
emailListRequest.LabelIds = "INBOX";
emailListRequest.IncludeSpamTrash = false;
//emailListRequest.Q = "is:unread"; // This was added because I only wanted unread emails...
// Get our emails
var emailListResponse = await emailListRequest.ExecuteAsync();
if (emailListResponse != null && emailListResponse.Messages != null)
{
// Loop through each email and get what fields you want...
foreach (var email in emailListResponse.Messages)
{
var emailInfoRequest = gmailService.Users.Messages.Get("EMAIL ADDRESS HERE", email.Id);
// Make another request for that email id...
var emailInfoResponse = await emailInfoRequest.ExecuteAsync();
if (emailInfoResponse != null)
{
String from = "";
String date = "";
String subject = "";
String body = "";
// Loop through the headers and get the fields we need...
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
{
date = mParts.Value;
}
else if(mParts.Name == "From" )
{
from = mParts.Value;
}
else if (mParts.Name == "Subject")
{
subject = mParts.Value;
}
if (date != "" && from != "")
{
if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
{
body = emailInfoResponse.Payload.Body.Data;
}
else
{
body = getNestedParts(emailInfoResponse.Payload.Parts, "");
}
// Need to replace some characters as the data for the email's body is base64
String codedBody = body.Replace("-", "+");
codedBody = codedBody.Replace("_", "/");
byte[] data = Convert.FromBase64String(codedBody);
body = Encoding.UTF8.GetString(data);
// Now you have the data you want...
}
}
}
}
}
}
catch (Exception)
{
MessageBox.Show("Failed to get messages!", "Failed Messages!", MessageBoxButtons.OK);
}
}
static String getNestedParts(IList<MessagePart> part, string curr)
{
string str = curr;
if (part == null)
{
return str;
}
else
{
foreach (var parts in part)
{
if (parts.Parts == null)
{
if (parts.Body != null && parts.Body.Data != null)
{
str += parts.Body.Data;
}
}
else
{
return getNestedParts(parts.Parts, str);
}
}
return str;
}
}
目前,此方法将检索所有电子邮件 ID,并为每个电子邮件 ID 获取每封电子邮件的 subject
、from
、date
和 body
。整个方法都有注释。如果有什么不明白的地方,请告诉我。另一个注意事项:在将其作为答案发布之前再次进行了测试。
抱歉,这不是一个答案,我无法在 Zaggler 的答案中添加评论(刚刚加入),所以只是 post 作为一个新答案,Zaggler 的答案很好,但是有一个小问题问题。当电子邮件 body 包含多个部分时。 Convert.FromBase64..... 不适用于两个连接的 base64 字符串。所以会发生异常。更好地转换然后加入 body 部分。
有人要代码,这里是完整的测试代码。其中大部分是从 Zaggler 复制的,但我最终有一些例外。所以我追查到上述问题。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
namespace GmailTests
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-dotnet-quickstart.json
static string[] Scopes = { GmailService.Scope.GmailModify };
static string ApplicationName = "Gmail API .NET Quickstart";
static void Main(string[] args)
{
UserCredential credential;
using (var stream =
new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/gmail-dotnet-quickstart2.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Gmail API service.
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var re = service.Users.Messages.List("me");
re.LabelIds = "INBOX";
re.Q = "is:unread"; //only get unread;
var res = re.Execute();
if (res != null && res.Messages != null)
{
Console.WriteLine("there are {0} emails. press any key to continue!", res.Messages.Count);
Console.ReadKey();
foreach (var email in res.Messages)
{
var emailInfoReq = service.Users.Messages.Get("me", email.Id);
var emailInfoResponse = emailInfoReq.Execute();
if (emailInfoResponse != null)
{
String from = "";
String date = "";
String subject = "";
String body = "";
//loop through the headers and get the fields we need...
foreach (var mParts in emailInfoResponse.Payload.Headers)
{
if (mParts.Name == "Date")
{
date = mParts.Value;
}
else if (mParts.Name == "From")
{
from = mParts.Value;
}
else if (mParts.Name == "Subject")
{
subject = mParts.Value;
}
if (date != "" && from != "")
{
if (emailInfoResponse.Payload.Parts == null && emailInfoResponse.Payload.Body != null)
body = DecodeBase64String(emailInfoResponse.Payload.Body.Data);
else
body = GetNestedBodyParts(emailInfoResponse.Payload.Parts, "");
//now you have the data you want....
}
}
//Console.Write(body);
Console.WriteLine("{0} -- {1} -- {2}", subject, date, email.Id);
Console.ReadKey();
}
}
}
}
static String DecodeBase64String(string s)
{
var ts = s.Replace("-", "+");
ts = ts.Replace("_", "/");
var bc = Convert.FromBase64String(ts);
var tts = Encoding.UTF8.GetString(bc);
return tts;
}
static String GetNestedBodyParts(IList<MessagePart> part, string curr)
{
string str = curr;
if (part == null)
{
return str;
}
else
{
foreach (var parts in part)
{
if (parts.Parts == null)
{
if (parts.Body != null && parts.Body.Data != null)
{
var ts = DecodeBase64String(parts.Body.Data);
str += ts;
}
}
else
{
return GetNestedBodyParts(parts.Parts, str);
}
}
return str;
}
}
}
}
首先:对@codexer 的回答投赞成票。
其次,在他的代码中使用以下函数来解码base64URL编码的主体。 Google 不仅对正文进行了 base64 编码,它还进行了 URL 编码 :-/
/// <summary>
/// Turn a URL encoded base64 encoded string into readable UTF-8 string.
/// </summary>
/// <param name="sInput">base64 URL ENCODED string.</param>
/// <returns>UTF-8 formatted string</returns>
private string DecodeURLEncodedBase64EncodedString(string sInput)
{
string sBase46codedBody = sInput.Replace("-", "+").Replace("_", "/").Replace("=", String.Empty); //get rid of URL encoding, and pull any current padding off.
string sPaddedBase46codedBody = sBase46codedBody.PadRight(sBase46codedBody.Length + (4 - sBase46codedBody.Length % 4) % 4, '='); //re-pad the string so it is correct length.
byte[] data = Convert.FromBase64String(sPaddedBase46codedBody);
return Encoding.UTF8.GetString(data);
}
UsersResource.MessagesResource.GetRequest getReq = null;
Google.Apis.Gmail.v1.Data.Message msg = null;
getReq = gmailServiceObj.Users.Messages.Get(userEmail, MessageID);
getReq.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
msg = getReq.Execute();
string converted = msg.Raw.Replace('-', '+');
converted = converted.Replace('_', '/');
byte[] decodedByte = Convert.FromBase64String(converted);
converted = null;
f_Path = Path.Combine(m_CloudParmsObj.m_strDestinationPath,MessageID + ".eml");
if (!Directory.Exists(m_CloudParmsObj.m_strDestinationPath))
Directory.CreateDirectory(m_CloudParmsObj.m_strDestinationPath);
// Create eml file
File.WriteAllBytes(f_Path, decodedByte);
我们可以得到包含所有消息属性的 .eml 文件。