如何使用Gmail API检索我的Gmail邮件?

我想要实现的目标:


我正在使用Gmail API ,基本上我想连接到我的GMail帐户来阅读我的电子邮件,INBOX类别,并获取每封邮件的基本信息(标题/主题, 日期和发件人)。

问题:


我正在尝试根据自己的需要调整这个用C#编写的Google示例,我正在寻找C#或Vb.Net的解决方案,无论如何。

(请注意,Google会针对不同的用户所示国家/地区显示不同的代码示例,因此该网页的代码可能与每个用户国家/地区的代码不同,Google的逻辑真的很糟糕。)

我在下面的代码中遇到的问题是:

  • 我在lblInbox.MessagesTotal属性中得到一个空值。
  • msgItem.Raw属性也总是空的。
  • 我还没有发现如何只解析INBOX类别中的消息。
  • 我还没有发现如何确定消息是读还是未读。
  • 我还没有发现如何确定邮件的基本信息(主题,从,到,日期,发件人)。

这是我尝试过的,请注意,在调整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对象的PayloadBody成员为null,所以我无法读取电子邮件。

我做错了什么?

 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 

目前由于某种原因,许多属性从任何请求返回null 。 如果我们有一个电子邮件ID列表,我们仍然可以解决这个问题。 然后,我们可以使用这些电子邮件ID并发送另一个请求以检索更多详细信息:来自日期,主题和正文。 @DalmTo也在正确的轨道上,但由于最近发生变化,所以标题不够近,这需要更多的请求。

 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 undread email's... //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 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获取每封电子邮件的subjectdatebody 。 整个方法都有评论,如果有什么你不明白的地方,请告诉我。 另一个注意事项: 在将此作为答案发布之前,再次对此进行了测试

对不起,这不是答案,我不能对Zaggler的答案添加评论(刚加入),所以只是发布作为一个新的答案,Zaggler的答案非常好,但是有一个小问题。 当电子邮件正文有多个部分时。 Convert.FromBase64 …..不适用于两个连接的base64字符串。 所以会发生exception。 更好的转换然后加入身体部位。

有人问代码,这是完成的测试代码。 他们中的大多数是从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 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编码的主体。 谷歌不仅base64编码的身体,它也是URL编码: – /

 ///  /// Turn a URL encoded base64 encoded string into readable UTF-8 string. ///  /// base64 URL ENCODED string. /// UTF-8 formatted string 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); } 

FileDatastore仅使用GoogleWebAuthorizationBroker.AuthorizeAsync中的用户参数来存储您的凭据,请查看我的教程Google .net – FileDatastore,以了解更多信息。

我的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返回可以循环遍历它们的消息对象列表。

Users.Messages包含应包含主题和to和from的标题。

我在gmail上也有一个非常古老的C#教程可能有所帮助。

更新以回答您的更新:

删除时会发生什么:

 .LabelIds = New Repeatable(Of String)({folder.Id}) 

labelIds string仅返回标签与所有指定标签ID匹配的消息。

您似乎正在发送文件夹ID。 尝试使用user.lables.list返回列出用户邮箱中的所有标签

 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文件。