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

时间:2016-04-06 10:20:35

标签: c# .net vb.net google-api gmail

我想要实现的目标:

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

问题:

我正在尝试根据自己的需要调整 this Google样本(用C#编写),我正在寻找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的集合,问题是{{ 1}}和Payload Body对象的成员为空,因此我无法阅读该电子邮件。

我做错了什么?。

newMsg

5 个答案:

答案 0 :(得分:22)

目前由于某种原因,许多属性都会从任何请求中返回null。如果我们有一个电子邮件ID列表,我们仍然可以解决这个问题。然后,我们可以使用这些电子邮件ID发送另一个请求以检索更多详细信息:fromdatesubjectbody@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 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获取每封电子邮件的subjectfromdatebody。整个方法都有评论。如果您有不明白的地方,请告诉我。另一方面,在将此作为答案发布之前再次进行了测试

答案 1 :(得分:9)

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

有人问代码,这是完成的测试代码。他们中的大多数是从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;
            }
        }
    }
}

答案 2 :(得分:2)

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

Users.Messages包含应包含主题和来往的标题。

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

更新以回复您的更新:

删除后会发生什么:

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

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

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

答案 3 :(得分:2)

首先:向上投票@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);
    }

答案 4 :(得分:1)

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