Liu
Liu

Reputation: 982

How to download attachment from email sent to google group mail address

Let's say my email [email protected] is in the google group which has email address [email protected]. We have a g suite service account which has enabled Google Apps Domain-wide Delegation. We have emails send from [email protected] to our group email [email protected], have subject Report from company b and attach the report in the email.

The issue is that the gmail api is able to list all messages but not able to list the attachments in each email.

Is there any way to do it?

Here's my code:

    // here when I create the client, I use my email address `[email protected]`
    using(var client = CreateClient())
    {
        UsersResource.MessagesResource.ListRequest request = client.Users.Messages.List("me");

        request.Q = "from:[email protected] AND subject:Report from company b AND has:attachment"

        // List messages.
        var messageIds = request.Execute().Messages?.Select(m => m.Id) ?? new List<string>();

        foreach(var mId in messageIds)
        {
            // https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get
            Message message = client.Users.Messages.Get("me", messageId).Execute();
            IList<MessagePart> parts = message.Payload.Parts;
            foreach (MessagePart part in parts)
            {
                if (!String.IsNullOrEmpty(part.Filename))
                {
                    String attId = part.Body.AttachmentId;
                    MessagePartBody attachPart = client.Users.Messages.Attachments.Get("me", messageId, attId).Execute();

                    // Converting from RFC 4648 base64 to base64url encoding
                    // see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
                    String attachData = attachPart.Data.Replace('-', '+');
                    attachData = attachData.Replace('_', '/');

                    byte[] data = Convert.FromBase64String(attachData);
                    var file = new FileInfo(part.Filename);
                    File.WriteAllBytes(file.FullName, data);
                }
            }
        }
    }

If I forward the mail manually to the same address (so the receiver will be me), the code downloads the attachment.

I'd appreciate if you can help.

Upvotes: 1

Views: 1384

Answers (1)

Liu
Liu

Reputation: 982

I've found the attachments are in the child MessagePart. So I wrote the recursive method to loop through all Parts to get all attachments.

    // List<FileInfo> Files = new List<FileInfo>();
    // client is created outside this method
    private void GetAttachmentsFromParts(IList<MessagePart> parts, string messageId)
    {
        if (parts == null) return;

        foreach (MessagePart part in parts)
        {
            if (!String.IsNullOrEmpty(part.Filename))
            {
                String attId = part.Body?.AttachmentId ?? null;
                if(String.IsNullOrWhiteSpace(attId)) continue;

                MessagePartBody attachPart = GmailServiceClient.Users.Messages.Attachments.Get("me", messageId, attId).Execute();

                // Converting from RFC 4648 base64 to base64url encoding
                // see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
                String attachData = attachPart.Data.Replace('-', '+');
                attachData = attachData.Replace('_', '/');

                byte[] data = Convert.FromBase64String(attachData);
                var file = new FileInfo(part.Filename);
                Files.Add(file);
                File.WriteAllBytes(file.FullName, data);
            }

            if((part.Parts?.Count ?? 0) > 0)
                GetAttachmentsFromParts(part.Parts, messageId);
        }
    }

All attachments will be stored in the List<FileInfo> Files

Upvotes: 1

Related Questions