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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36448193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 19:48:04  来源:igfitidea点击:

How to retrieve my Gmail messages using Gmail API?

c#.netvb.netgoogle-apigmail

提问by ElektroStudios

What I want to achieve:

我想要达到的目标:



I'm using the Gmail APIand basically I would like to connect to my GMail account to read my emails, of INBOX category, and get basic info for each message (title/subject, from, to, date, and the sender).

我使用的是Gmail的API,基本上我想连接到我的Gmail帐户阅读我的邮件,收件箱类别,并获取每个邮件的基本信息(标题/主题,日期和发送者)。

Problems:

问题:



I'm trying to adapt thisGoogle sample, written in C#, to my own needs, I'm searching for a solution in C# or Vb.Net, no matter.

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

(Be aware that Google shows different code examples for different user-countries, so the code of that webpage maybe will not be the same for every one, that Google's logic really sucks.)

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

The problems I have with the code below, are these:

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

  • I'm getting an empty value in lblInbox.MessagesTotalproperty.
  • msgItem.Rawproperty is always empty too.
  • I haven't yet discovered how to parse only the messages that are inside the INBOX category.
  • I haven't yet discovered how to determine if a message is read or unread.
  • I haven't yet discovered how to determine the basic info of a message (subject, from, to, date, sender).
  • 我在lblInbox.MessagesTotal财产中得到一个空值。
  • msgItem.Raw财产也总是空的。
  • 我还没有发现如何只解析收件箱类别内的消息。
  • 我还没有发现如何确定消息是已读还是未读。
  • 我还没有发现如何确定消息的基本信息(主题、发件人、收件人、日期、发件人)。

This is what I've tried, note that when adapting the Google's sample, I assumed that "user"argument should be the Gmail user account name ("[email protected]"), but I'm not sure it should be that.

这是我尝试过的,请注意,在调整 Google 的示例时,我假设"user"参数应该是 Gmail 用户帐户名 ( "[email protected]"),但我不确定应该是那个。

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},
                                                                           "[email protected]",
                                                                           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

Question:

题:



I will ask for the most important need (however, any help to solve the other mentioned problems are very welcome):

我会问最重要的需求(但是,非常欢迎对解决其他提到的问题的任何帮助):

  • In C# or VB.Net, how can I obtain a collection to iterate allthe emails that are in the INBOX group?.
  • 在 C# 或 VB.Net 中,如何获取集合以迭代INBOX 组中的所有电子邮件?。


Update:

更新:

This is the code that I'm using right now, the intention is to retrieve a collection of all Messages of the specified mailbox label, the problem is that the Payloadand Bodymember of newMsgobject is null, so I can't read the email.

这是我现在正在使用的代码,目的是检索Message指定邮箱label的所有s 的集合,问题是object的PayloadandBody成员newMsg为空,所以我无法阅读电子邮件。

What I'm doing wrong?.

我做错了什么?

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

回答by ???ěxě?

Currently for some reason or another many of the properties are coming back nullfrom any of the requests. We can still get around that if we have a list of the email ids. We then can use these email ids to send out another request to retrieve further details: from, date, subject, and body. @DalmTowas on the right track as well, but not close enough about the headers as it has changed recently which will require a few more requests.

目前,由于某种原因或其他原因,许多属性正在null从任何请求中返回。如果我们有电子邮件 ID 列表,我们仍然可以解决这个问题。然后,我们可以使用这些电子邮件ID发出另一个请求以获取进一步的细节:fromdatesubject,和body@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;
    }        
}

Currently, this method will retrieve all email ids and for each email id get the subject,from, dateand bodyof each email. There are comments throughout the method. If there is something you do not understand, please let me know. On another note: this was tested again before posting this as an answer.

目前,这种方法会检索所有电子邮件ID,并为每个电子邮件ID得到subjectfromdatebody每封电子邮件。整个方法都有注释。如果您有什么不明白的地方,请告诉我。另一个注意事项:在将此作为答案发布之前再次进行了测试

回答by YanXi

sorry this is not an answer, i can't add a comment to Zaggler's answer(just joined), so just post as a new answer, Zaggler's answer is very good, but there is a small problem. when the email body has more then one part. the Convert.FromBase64..... doesn't work on two joined base64 strings. so an exception will be occure. better convert then joined the body parts.

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

some one ask for the code, and here is the completed tested code. most of them are copied from Zaggler, but i end up with some exceptions. so i traced down to the problem described above.

有人要代码,这里是完整的测试代码。其中大部分是从 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;
            }
        }
    }
}

回答by mike

First: up-vote @codexer 's answer.

第一:投票@codexer 的答案。

Second, use the following function in his code to decode the base64URL encoded body. Google not only base64 encoded the body, it is also URL encoded :-/

其次,在他的代码中使用下面的函数来解码base64URL编码的body。谷歌不仅对正文进行了 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);
    }

回答by DaImTo

The user parameter in GoogleWebAuthorizationBroker.AuthorizeAsync is just used by FileDatastore to store your credentials check my tutorial Google .net – FileDatastore demystifiedfor more information.

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

My VB.net is very rusty like 6 years rusty but in C# you could do something like this

我的 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 a list of message objects you can loop though them.

MessageResource.ListRequest 返回一个消息对象列表,您可以通过它们循环。

Users.Messagescontains header which should have the subject and the to and from.

Users.Messages包含标题,该标题应具有主题和收件人

I also have a really old C# tutorial on gmailthat might help.

我还有一个关于gmail的非常古老的 C# 教程,可能会有所帮助。

Update to answer your update:

更新以回答您的更新:

What happens when you remove:

删除后会发生什么:

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

labelIds string Only return messages with labels that match all of the specified label IDs.

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

It appears you are sending a folder id. try using user.lables.listwhich returns Lists all labels in the user's mailbox

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

回答by Renuka

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);

We can get the .eml file with all message properties like this.

我们可以像这样获取包含所有消息属性的 .eml 文件。