I am integrating an API in react native, where I am facing a parsing error. But, from the postman, it works fine.
I have tried to send an image through uri, blob, base64 string but facing the same issue.
Below is my react-native code:
var data = new FormData();
data.append("ImgInput", "/IMG_20161229_140335993.jpg");
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "http://212.48.69.65:7465/Core.svc/UploadImage?ApplicationName=www_spoiltpigrewards_com&SessionToken=7E2BE649-3136-4074-9B88-670D717C5828&UserName=jatin1#gmail.com&StoreName=Cooperative&NumberOfProducts=2&Multishot=true&MultishotGuid=00000000-0000-0000-0000-000000000000&FileNumber=1&MaxFileNumber=2");
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("cache-control", "no-cache");
xhr.send(data);
below is postman collection in json format. it works fine. I want same output from react-native code.
{
"info": {
"_postman_id": "7a0149d0-c96c-4431-8978-1b619968edf7",
"name": "SpoiltPig",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "http://212.48.69.65:7465/Core.svc/UploadImage?ApplicationName=www_spoiltpigrewards_com&SessionToken=7E2BE649-3136-4074-9B88-670D717C5828&UserName=jatin1#gmail.com&StoreName=Cooperative&NumberOfProducts=2&Multishot=true&MultishotGuid=00000000-0000-0000-0000-000000000000&FileNumber=1&MaxFileNumber=2",
"request": {
"method": "POST",
"header": [
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "ImgInput",
"value": null,
"type": "file"
}
]
},
"url": {
"raw": "http://212.48.69.65:7465/Core.svc/UploadImage?ApplicationName=www_spoiltpigrewards_com&SessionToken=7E2BE649-3136-4074-9B88-670D717C5828&UserName=jatin1#gmail.com&StoreName=Cooperative&NumberOfProducts=2&Multishot=true&MultishotGuid=00000000-0000-0000-0000-000000000000&FileNumber=1&MaxFileNumber=2",
"protocol": "http",
"host": [
"212",
"48",
"69",
"65"
],
"port": "7465",
"path": [
"Core.svc",
"UploadImage"
],
"query": [
{
"key": "ApplicationName",
"value": "www_spoiltpigrewards_com"
},
{
"key": "SessionToken",
"value": "7E2BE649-3136-4074-9B88-670D717C5828"
},
{
"key": "UserName",
"value": "jatin1#gmail.com"
},
{
"key": "StoreName",
"value": "Cooperative"
},
{
"key": "NumberOfProducts",
"value": "2"
},
{
"key": "Multishot",
"value": "true"
},
{
"key": "MultishotGuid",
"value": "00000000-0000-0000-0000-000000000000"
},
{
"key": "FileNumber",
"value": "1"
},
{
"key": "MaxFileNumber",
"value": "2"
}
]
}
},
"response": []
}
]
}
I am making a request to the User.messages endpoint. All objects returned (the emails) have a mimeType property which I'm struggling to understand.
More specifically, I want to be able to extract the body of the email depending of the mimeType since I've been able to notice that depending on the mimeType, the body will be inside the body property in payload, or in the parts array. What are the different mimeTypes that can be returned, and where can I find the body of the email for each one of them?
I think it will make sense if you think of the payload as a part in of itself. Let's say I send a message with just a subject and a plain message text:
From: emtholin#gmail.com
To: emtholin#gmail.com
Subject: Example Subject
This is the plain text message
This will result in the following parsed message:
{
"id": "154ecb53c10b74d8",
"threadId": "154ecb53c10b74d8",
"labelIds": [
"INBOX",
"SENT"
],
"snippet": "This is the plain text message",
"historyId": "38877",
"internalDate": "1464260181000",
"payload": {
"partId": "",
"mimeType": "text/plain",
"filename": "",
"headers": [
...
],
"body": {
"size": 31,
"data": "VGhpcyBpcyB0aGUgcGxhaW4gdGV4dCBtZXNzYWdlCg=="
}
},
"sizeEstimate": 355
}
If I send a message with a plain text part, a html part and an image, it will look like this when parsed:
{
"id": "154ed5ccaa12f3df",
"threadId": "154ed5ccaa12f3df",
"labelIds": [
"SENT",
"INBOX",
"IMPORTANT"
],
"snippet": "This is a plain/html message with an image.",
"historyId": "841379",
"internalDate": "1464271162000",
"payload": {
"mimeType": "multipart/mixed",
"filename": "",
"headers": [
...
],
"body": {
"size": 0
},
"parts": [
{
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=089e0122896c7c80d80533bf3205"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
}
],
"body": {
"size": 47,
"data": "VGhpcyBpcyBhIHBsYWluL2h0bWwgKm1lc3NhZ2UqIHdpdGggYW4gaW1hZ2UuDQo="
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
}
],
"body": {
"size": 73,
"data": "PGRpdiBkaXI9Imx0ciI-VGhpcyBpcyBhIHBsYWluL2h0bWwgPGI-bWVzc2FnZTwvYj4gd2l0aCBhbiBpbWFnZS48L2Rpdj4NCg=="
}
}
]
},
{
"partId": "1",
"mimeType": "image/png",
"filename": "smile.png",
"headers": [
...
],
"body": {
"attachmentId": "ANGjdJ-OrSy7VAYL-UbRyNtmySbZLlV-fV43zJF0_neNGZ8yKugsZAxb32eSb-CrbYIhF9NvjGwBVEjSkRrUWoCS7aDpgoQnt9WR7f2sa17qVEyOg_JVSbrGrunirvQw2dY-SxxB3Y0JP3aYDHSBXpNO6fFCByVFWQDw1et5Mh9di7bGO4AWOLKFVe_Yb2RmdDwuazGXGb8zA88TTMaiEPIacPTNiVtBrIWG0EKGxHBhep9j8ujyWeCS5P9X80dBHvBNj4T9XjUwcrN6FvwegRewRMM9cBupY7jQESR7915OcbhCNyi5l64x6vVh1ZU",
"size": 2002
}
}
]
},
"sizeEstimate": 3077
}
You will see it's just the RFC822-message parsed to JSON. If you just traverse the parts, and treat the payload as a part itself, you will find what you are looking for.
var parts = [response.payload];
while (parts.length) {
var part = parts.shift();
if (part.parts) {
parts = parts.concat(part.parts);
}
if(part.mimeType === 'text/html') {
var decodedPart = decodeURIComponent(escape(atob(part.body.data.replace(/\-/g, '+').replace(/\_/g, '/'))));
console.log(decodedPart);
}
}
There are many MIME types that can be returned, here are a few:
text/plain: the message body only in plain text
text/html: the message body only in HTML
multipart/alternative: will contain two parts that are alternatives for each othe, for example:
a text/plain part for the message body in plain text
a text/html part for the message body in html
multipart/mixed: will contain many unrelated parts which can be:
multipart/alternative as above, or text/plain or text/html as above
application/octet-stream, or other application/* for application specific mime types for attachments
image/png ot other image/* for images, which could be embedded in the message.
The definitive reference for all this is RFC 2046 https://www.ietf.org/rfc/rfc2046.txt (you might want to also see 2044 and 2045)
To answer your question, build a tree of the message, and look either for:
the first text/plain or text/html part (either in the message body or in a multipart/mixed)
the first text/plain or text/html inside of a multipart/alternative, which may be part of a multipart mixed.
An example of a complex message:
multipart/mixed
multipart/alternative
text/plain <- message body in plain text
text/html <- message body in HTML
application/zip <- a zip file attachment
-
I know this question is not new but I've wrote a PHP script which correctly parses messages pulled from Gmail API, including any type of attachment.
The script includes a recursive "iterateParts" function which iterates all message parts so we can be sure we extracted all available data from each message.
Script steps are:
Pull all message ids from API
Get some important headers (subject & from address)
Either body is directly on payload or send payload to iterateParts
iterateParts is parsing each message to $msgArr with it's data, base64 encoded
Push $msgArr to master array $allmsgArr
Traverse master array and save each part as file according to it's MIME type and filename
$maxToPull = 1;
$gmailQuery = "ALL";
// Initializing Google API
$service = new Google_Service_Gmail($client);
// Pulling all gmail messages into $messages array
$user = 'me';
$msglist = $service->users_messages->listUsersMessages($user, ["maxResults"=>$maxToPull, "q"=>$gmailQuery]);
$messages = $msglist->getMessages();
// Master array that will hold all parsed messages data, including attachments
$allmsgArr = array();
// Traverse each message
foreach($messages as $message)
{
$msgArr = array();
$single_message = $service->users_messages->get('me', $message->getId());
$payload = $single_message->getPayload();
// Nice to have the gmail msg id, can be used to direct access the message in Gmail's web gui
$msgArr['gmailmsgid'] = $message->getId();
// Retrieving the subject and "from" email address
foreach($payload->getheaders() as $oneheader)
{
if($oneheader['name'] == 'Subject')
$msgArr['subject'] = $oneheader['value'];
if($oneheader['name'] == 'From')
$msgArr['fromaddress'] = substr($oneheader['value'], strpos($oneheader['value'], '<')+1, -1);
}
// If body is directly in the message payload (only for plain text messages where there's no HTML part and no attachments, normally this is not the case)
if($payload['body']['size'] > 0)
$msgArr['textplain'] = $payload['body']['data'];
// Else, iterate over each message part and continue to dig if necessary
else
iterateParts($payload, $message->getId());
// Push the parsed $msgArr (parsed by iterateParts) to master array
array_push($allmsgArr, $msgArr);
}
// Traverse each parsed message and saving it's content and attachments to files
foreach($allmsgArr as $onemsgArr)
{
$folder = "messages/".$onemsgArr['gmailmsgid'];
mkdir($folder);
if($onemsgArr['textplain'])
file_put_contents($folder."/textplain.txt", decodeData($onemsgArr['textplain']));
if($onemsgArr['texthtml'])
file_put_contents($folder."/texthtml.html", decodeData($onemsgArr['texthtml']));
if($onemsgArr['attachments'])
{
foreach($onemsgArr['attachments'] as $oneattachment)
{
if(!empty($oneattachment['filename']))
$filename = $oneattachment['filename'];
else if($oneattachment['mimetype'] == "message/rfc822" && empty($oneattachment['filename'])) // email attachments
$filename = "noname.eml";
else
$filename = "unknown";
file_put_contents($folder."/".$filename, decodeData($oneattachment['data']));
}
}
}
function iterateParts($obj, $msgid) {
global $msgArr;
global $service;
foreach($obj as $parts)
{
// if found body data
if($parts['body']['size'] > 0)
{
// plain text representation of message body
if($parts['mimeType'] == 'text/plain')
{
$msgArr['textplain'] = $parts['body']['data'];
}
// html representation of message body
else if($parts['mimeType'] == 'text/html')
{
$msgArr['texthtml'] = $parts['body']['data'];
}
// if it's an attachment
else if(!empty($parts['body']['attachmentId']))
{
$attachArr['mimetype'] = $parts['mimeType'];
$attachArr['filename'] = $parts['filename'];
$attachArr['attachmentId'] = $parts['body']['attachmentId'];
// the message holds the attachment id, retrieve it's data from users_messages_attachments
$attachmentId_base64 = $parts['body']['attachmentId'];
$single_attachment = $service->users_messages_attachments->get('me', $msgid, $attachmentId_base64);
$attachArr['data'] = $single_attachment->getData();
$msgArr['attachments'][] = $attachArr;
}
}
// if there are other parts inside, go get them
if(!empty($parts['parts']) && !empty($parts['mimeType']) && empty($parts['body']['attachmentId']))
{
iterateParts($parts->getParts(), $msgid);
}
}
}
// All data returned from API is base64 encoded
function decodeData($data)
{
$sanitizedData = strtr($data,'-_', '+/');
return base64_decode($sanitizedData);
}
This is how $allmsgArr will look like (where only one message was pulled):
Array
(
[0] => Array
(
[gmailmsgid] => 25k1asfa556x2da
[fromaddress] => john#gmail.com
[subject] => Fwd: Sea gulls picture
[textplain] => UE5SIDQxQzAwMg0KDQpBUkJFTFRFU1QxDQoNCg0K
[texthtml] => PGRpdiBkaXI9Imx0ciI-PHNwYW4gc3R5bGU9ImZi
[attachments] => Array
(
[0] => Array
(
[mimetype] => image/png
[filename] => sea_gulls.png
[attachmentId] => ANGjdJ9tmy4d8vPXhU_BjNEFEaDODOpu29W2u5OTM7a0
[data] => iVBORw0KGgoAAAANSUhEUgAABSYAAAKWCAYAAABUP
)
[1] => Array
(
[mimetype] => image/jpeg
[filename] => Outlook_Signature.jpg
[attachmentId] => ANGjdJ-CgZTK0oK44Q8j7TlN_JlaexxGKZ_wHFfoEB
[data] => 6jRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEa
)
)
)
)
Based on the Tholle idea, I've completed his script to extract Gmail body and attachments.
First of all, you should fetch any gmail-message object and then parse it.
You can fetch any gmail-message with this code:
const {google} = require('googleapis')
// do your authenticatoin here
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectTo)
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
const response = await this.gmail.users.messages.get({
auth: oAuth2Client,
userId: 'me',
id: messageId,
format: 'full'
})
const message_obj = response.data
Main Script:
function parser(response) {
function decode(input) {
const text = new Buffer.from(input, 'base64').toString('ascii')
return decodeURIComponent(escape(text))
}
function decode_alternative(input) {
// this way does not escape special "B" characters
// const text = Buffer.from(input, 'base64').toString('ascii')
// return decodeURIComponent(escape(text))
return base64.decode(input.replace(/-/g, '+').replace(/_/g, '/'))
}
const result = {
text: '',
html: '',
attachments: []
}
let parts = [response.payload]
while (parts.length) {
let part = parts.shift()
if (part.parts)
parts = parts.concat(part.parts)
if (part.mimeType === 'text/plain')
result.text = decode(part.body.data)
if (part.mimeType === 'text/html')
result.html = decode(part.body.data)
if (part.body.attachmentId) {
result.attachments.push({
'partId': part.partId,
'mimeType': part.mimeType,
'filename': part.filename,
'body': part.body
})
}
}
return result
}
Sample Data and response:
const with_multi_type_attachments = {
"id": "16c624e85dfd9883",
"threadId": "16c62397458f34b1",
"labelIds": [],
"snippet": "This is body. Inline-attachments my-custom-link my-custom-email-address Emoji: 😂😎 closure.mp4",
"historyId": "14006828",
"internalDate": "1565017407000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 261,
"data": "VGhpcyBpcyBib2R5Lg0KDQpJbmxpbmUtYXR0YWNobWVudHMNCltpbWFnZTogMTMuanBnXQ0KbXktY3VzdG9tLWxpbmsgPGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS8-DQpteS1jdXN0b20tZW1haWwtYWRkcmVzcyA8bWVAd29yay5jb20-DQoNCkVtb2ppOg0K8J-YgvCfmI4NCg0KIGNsb3N1cmUubXA0DQo8aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xVXo4cHpoamh4UC13b0c5dFFkaGM2R1h2RGwzYUpSS3Uvdmlldz91c3A9ZHJpdmVfd2ViPg0K"
}
},
{
"partId": "0.0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 1244,
"data": "PGRpdiBkaXI9Imx0ciI-PGJyPjxkaXYgY2xhc3M9ImdtYWlsX3F1b3RlIj48ZGl2IGRpcj0ibHRyIj48ZGl2PlRoaXMgaXMgYm9keS48L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2PklubGluZS1hdHRhY2htZW50czxicj48L2Rpdj48ZGl2PjxkaXY-PGltZyBzcmM9ImNpZDppaV9qeXlpNjgyNjAiIGFsdD0iMTMuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OjBweCIgd2lkdGg9IjIyNSIgaGVpZ2h0PSIyMjUiPjxicj48YSBocmVmPSJodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vIiB0YXJnZXQ9Il9ibGFuayI-bXktY3VzdG9tLWxpbms8L2E-PGEgaHJlZj0ibWFpbHRvOm1lQHdvcmsuY29tIiB0YXJnZXQ9Il9ibGFuayI-PGJyPm15LWN1c3RvbS1lbWFpbC1hZGRyZXNzPC9hPjwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-RW1vamk6PGJyPjwvZGl2PjxkaXY-8J-YgvCfmI48L2Rpdj48ZGl2Pjxicj48L2Rpdj48ZGl2IGNsYXNzPSJnbWFpbF9jaGlwIGdtYWlsX2RyaXZlX2NoaXAiIHN0eWxlPSJ3aWR0aDozOTZweDtoZWlnaHQ6MThweDttYXgtaGVpZ2h0OjE4cHg7YmFja2dyb3VuZC1jb2xvcjojZjVmNWY1O3BhZGRpbmc6NXB4O2NvbG9yOiMyMjI7Zm9udC1mYW1pbHk6YXJpYWw7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC13ZWlnaHQ6Ym9sZDtmb250LXNpemU6MTNweDtib3JkZXI6MXB4IHNvbGlkICNkZGQ7bGluZS1oZWlnaHQ6MSIgY29udGVudGVkaXRhYmxlPSJmYWxzZSI-PGEgaHJlZj0iaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xVXo4cHpoamh4UC13b0c5dFFkaGM2R1h2RGwzYUpSS3Uvdmlldz91c3A9ZHJpdmVfd2ViIiB0YXJnZXQ9Il9ibGFuayIgc3R5bGU9ImRpc3BsYXk6aW5saW5lLWJsb2NrO21heC13aWR0aDozNjZweDtvdmVyZmxvdzpoaWRkZW47dGV4dC1vdmVyZmxvdzplbGxpcHNpczt3aGl0ZS1zcGFjZTpub3dyYXA7dGV4dC1kZWNvcmF0aW9uOm5vbmU7cGFkZGluZzoxcHggMDtib3JkZXI6bm9uZSI-PGltZyBzdHlsZT0idmVydGljYWwtYWxpZ246IGJvdHRvbTsgYm9yZGVyOiBub25lOyIgc3JjPSJodHRwczovL3NzbC5nc3RhdGljLmNvbS9kb2NzL2RvY2xpc3QvaW1hZ2VzL2ljb25fMTBfZ2VuZXJpY19saXN0LnBuZyI-wqA8c3BhbiBkaXI9Imx0ciIgc3R5bGU9ImNvbG9yOiMxNWM7dGV4dC1kZWNvcmF0aW9uOm5vbmU7dmVydGljYWwtYWxpZ246Ym90dG9tIj5jbG9zdXJlLm1wNDwvc3Bhbj48L2E-PC9kaXY-PC9kaXY-PC9kaXY-PC9kaXY-PC9kaXY-DQo="
}
}
]
},
{
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "13.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-BkrF-Gf3L_44M9RIu8clbEbFwu0xLlfL3YOEja7y5pxZecT7YfI6hnI6-PvJ9G-w6orcWYN9jgYAOsHBCBFHezwihjdVWKBYkGB9gmdCBFVK0XKHhGODyMPJLTW2kbbhyTLFBDjEo33Tld7XMtbRAvULGV_Z6mue8XdNAfxcAnhNOGZ48Pi7y_IugKIIXZ_DAD3JvALMxctRONavk7_-oOtf09ksIQPshaA",
"size": 58159
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attachment-1.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ_M5qM10ANxuQiqn7xLsqGiB01ZfnkpzDd186JRI7NxB7l9nA2xN3EmhKygGyG4aSWeHiZeo-R9NXDG-HKWIDnuOwLoAiLLggvsQ5qlXwsGhKS7f383YaVJs8joI1Q5JtsepWPkBaBCR2wPviMt4mX_L5M7Em8GzKVtLh7fuPFbXguaHIUoyhCXP6mnKXQmNiyaatPvlB7_KPgi7h5wD9gDctpPSu59mUs-9Q",
"size": 707870
}
},
{
"partId": "2",
"mimeType": "video/mp4",
"filename": "closure.mp4",
"headers": [],
"body": {
"attachmentId": "ANGjdJ9iIPaAkkhk7mNFzOU5lQuNMRmiMxgZVY1NDsO-p6XvvbyoEqNgRqz4pepUK5HcGpOGJRMHB0ec9_wlHYbNfhr6aPvdIRO-VD4-Baw55yHgZ8KWhHAiZ3l-BY5nTB6B1xpRwEqKoun52EkPDCRf8g87tEOtq7p4ut02hg",
"size": 7261025
}
}
]
},
"sizeEstimate": 10987809
}
const with_attachmetnst = {
"id": "16c54135a9d42ab4",
"threadId": "16c5411a8c7fcaa8",
"labelIds": [],
"snippet": "With-Attachment-Body droped-ing.jpg",
"historyId": "14005159",
"internalDate": "1564778649000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 75,
"data": "V2l0aC1BdHRhY2htZW50LUJvZHkNCg0KDQpbaW1hZ2U6IGRyb3BlZC1pbmcuanBnXSA8aHR0cDovL2Ryb3BlZC1pbmcuanBnPg0K"
}
},
{
"partId": "0.0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 247,
"data": "PGRpdiBkaXI9Imx0ciI-PGRpdj5XaXRoLUF0dGFjaG1lbnQtQm9keTwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-PGJyPjwvZGl2PjxkaXY-PGRpdj48YSBocmVmPSJodHRwOi8vZHJvcGVkLWluZy5qcGciPjxpbWcgc3JjPSJjaWQ6aWlfanl1a3M4NnQwIiBhbHQ9ImRyb3BlZC1pbmcuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OiAwcHg7IiB3aWR0aD0iMjIzIiBoZWlnaHQ9IjIyMyI-PC9hPjxicj48L2Rpdj48L2Rpdj48L2Rpdj4NCg=="
}
}
]
},
{
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "droped-ing.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-hNpJEK_gzrYEsdQODp-Zwe5QoJP5ONsOy5JoSUC1Qk5Po7KgL_AJnMddPRZ1GWpltr-XRtXv3zS5TTUYJxf7BBZyitkMNH9Kv_rnArqXLJyBOqfL1wNqAJPeQrCzUjk6d0ahqAl6ixNyCCgTu-fxvngaBllXU5pTI3_iL6jWXoin6LoQ-a32vhKs319tChOz5GBuTCTov9oZqTtJPvj1yIqLAmUf8vochDQ",
"size": 43716
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attached-img.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-wHL_YHq_zUYZ4AyCHdstehG_7lhh21SXnzvf_33oECSiFcua3UTEbO2u5gSrEVDS4xvdnQa4e2JFb5olkMbv8rBuAprlADc_99pG_X-kf9gjhCiEIPPpr66S7VkB2Wumh9rBFc0bN6j_8mEjoGEBDAyd7lb38SiY8A6v2TP2o9gaKucYfIB__tiQ4Z1C-pSipyNmToCJfE87TuFp_ukQtDQbrVyG1bEoy2w",
"size": 44988
}
},
{
"partId": "2",
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"filename": "Motivation Letter Sample.docx",
"headers": [],
"body": {
"attachmentId": "ANGjdJ9c8IKsgC8Do8iAyDHHGiyiho_zv8eI4BkaW_kvnl7--webl9P01xGWTyCwTRblgClK6dE2JH8P7hNd007ZK-CIU5Stwuc6Mp2v-KHC3slmCfko--JRrU1-EotFJqrnr3gmRw9qZgWlCqKxqiJAo1afE67LtwBSuR_frjCCUPH7RuxdY6mP_WJHSP6XA6i5uyhWaRGpnMOzawbTdq1_ZSKo9mjF__dDOsdrlQ",
"size": 7136
}
}
]
},
"sizeEstimate": 133087
}
const without_attachmetnst = {
"id": "16c579bc78fc11b1",
"threadId": "16c579b9b32bc8c5",
"labelIds": [],
"snippet": "Its Body",
"historyId": "14005084",
"internalDate": "1564837921000",
"payload": {
"partId": "",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 10,
"data": "SXRzIEJvZHkNCg=="
}
},
{
"partId": "1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 47,
"data": "PGRpdiBkaXI9Imx0ciI-SXRzIEJvZHk8YnIgY2xlYXI9ImFsbCI-PC9kaXY-DQo="
}
}
]
},
"sizeEstimate": 584
}
const only_inline_attachment = {
"id": "16c61fb94d1b287f",
"threadId": "16c60a9c8f51833b",
"labelIds": [],
"snippet": "Just-has-inline-attachments-Body",
"historyId": "14005012",
"internalDate": "1565011972000",
"payload": {
"partId": "",
"mimeType": "multipart/related",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/plain",
"filename": "",
"headers": [],
"body": {
"size": 78,
"data": "SnVzdC1oYXMtaW5saW5lLWF0dGFjaG1lbnRzLUJvZHkNCg0KDQpbaW1hZ2U6IEJyb256ZS1GbG9vci1MYW1wLTYwMHg2MDAuanBnXQ0K"
}
},
{
"partId": "0.1",
"mimeType": "text/html",
"filename": "",
"headers": [],
"body": {
"size": 294,
"data": "PGRpdiBkaXI9Imx0ciI-PGJyPjxkaXYgY2xhc3M9ImdtYWlsX3F1b3RlIj48ZGl2IGRpcj0ibHRyIj48ZGl2Pkp1c3QtaGFzLWlubGluZS1hdHRhY2htZW50cy1Cb2R5PC9kaXY-PGRpdj48YnI-PC9kaXY-PGRpdj48ZGl2Pjxicj48ZGl2PjxpbWcgc3JjPSJjaWQ6aWlfanl5MmtjN2IwIiBhbHQ9IkJyb256ZS1GbG9vci1MYW1wLTYwMHg2MDAuanBnIiBzdHlsZT0ibWFyZ2luLXJpZ2h0OjBweCIgd2lkdGg9IjIyNSIgaGVpZ2h0PSIyMjUiPjxicj48L2Rpdj48L2Rpdj48L2Rpdj48L2Rpdj4NCjwvZGl2PjwvZGl2Pg0K"
}
}
]
},
{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "Bronze-Floor-Lamp-600x600.jpg",
"headers": [],
"body": {
"attachmentId": "ANGjdJ-DX3652q5kW-6nNaEQL-q2zvx1Df2FV-GOfx7YfzcY2NnVkM4uMmr058QUaiAX4wsI3LybFtMB6Xaqy6ijsx2RAUg56nESumo2ecDvs-3PUgDshHJgHluwcEmpDIh7H9w6TEHteDDAs9v4jcu5xX-2_GNyeKUVK8BfGY-qYvgwVwWL5fg-TaiZ6SyrtB_w8dSOpQwtw_at25oeGRpmMh7qkLrbZ6yAkfMIXw",
"size": 91648
}
}
]
},
"sizeEstimate": 126975
}
parser(with_multi_type_attachments)
parser(with_attachmetnst)
parser(without_attachmetnst)
parser(only_inline_attachment)
Result
with_multi_type_attachments: {
"text": "This is body.\r\n\r\nInline-attachments\r\n[image: 13.jpg]\r\nmy-custom-link <http://stackoverflow.com/>\r\nmy-custom-email-address <me#work.com>\r\n\r\nEmoji:\r\np\u001f\u0018\u0002p\u001f\u0018\u000e\r\n\r\n closure.mp4\r\n<https://drive.google.com/file/d/1Uz8pzhjhxP-woG9tQdhc6GXvDl3aJRKu/view?usp=drive_web>\r\n",
"html": "<div dir=\"ltr\"><br><div class=\"gmail_quote\"><div dir=\"ltr\"><div>This is body.</div><div><br></div><div>Inline-attachments<br></div><div><div><img src=\"cid:ii_jyyi68260\" alt=\"13.jpg\" style=\"margin-right:0px\" width=\"225\" height=\"225\"><br>my-custom-link<br>my-custom-email-address</div><div><br></div><div>Emoji:<br></div><div>p\u001f\u0018\u0002p\u001f\u0018\u000e</div><div><br></div><div class=\"gmail_chip gmail_drive_chip\" style=\"width:396px;height:18px;max-height:18px;background-color:#f5f5f5;padding:5px;color:#222;font-family:arial;font-style:normal;font-weight:bold;font-size:13px;border:1px solid #ddd;line-height:1\" contenteditable=\"false\"><img style=\"vertical-align: bottom; border: none;\" src=\"https://ssl.gstatic.com/docs/doclist/images/icon_10_generic_list.png\">B <span dir=\"ltr\" style=\"color:#15c;text-decoration:none;vertical-align:bottom\">closure.mp4</span></div></div></div></div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attachment-1.jpg",
"body": {
"attachmentId": "ANGjdJ_M5qM10ANxuQiqn7xLsqGiB01ZfnkpzDd186JRI7NxB7l9nA2xN3EmhKygGyG4aSWeHiZeo-R9NXDG-HKWIDnuOwLoAiLLggvsQ5qlXwsGhKS7f383YaVJs8joI1Q5JtsepWPkBaBCR2wPviMt4mX_L5M7Em8GzKVtLh7fuPFbXguaHIUoyhCXP6mnKXQmNiyaatPvlB7_KPgi7h5wD9gDctpPSu59mUs-9Q",
"size": 707870
}
}, {
"partId": "2",
"mimeType": "video/mp4",
"filename": "closure.mp4",
"body": {
"attachmentId": "ANGjdJ9iIPaAkkhk7mNFzOU5lQuNMRmiMxgZVY1NDsO-p6XvvbyoEqNgRqz4pepUK5HcGpOGJRMHB0ec9_wlHYbNfhr6aPvdIRO-VD4-Baw55yHgZ8KWhHAiZ3l-BY5nTB6B1xpRwEqKoun52EkPDCRf8g87tEOtq7p4ut02hg",
"size": 7261025
}
}, {
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "13.jpg",
"body": {
"attachmentId": "ANGjdJ-BkrF-Gf3L_44M9RIu8clbEbFwu0xLlfL3YOEja7y5pxZecT7YfI6hnI6-PvJ9G-w6orcWYN9jgYAOsHBCBFHezwihjdVWKBYkGB9gmdCBFVK0XKHhGODyMPJLTW2kbbhyTLFBDjEo33Tld7XMtbRAvULGV_Z6mue8XdNAfxcAnhNOGZ48Pi7y_IugKIIXZ_DAD3JvALMxctRONavk7_-oOtf09ksIQPshaA",
"size": 58159
}
}]
}
with_attachmetnst: {
"text": "With-Attachment-Body\r\n\r\n\r\n[image: droped-ing.jpg] <http://droped-ing.jpg>\r\n",
"html": "<div dir=\"ltr\"><div>With-Attachment-Body</div><div><br></div><div><br></div><div><div><img src=\"cid:ii_jyuks86t0\" alt=\"droped-ing.jpg\" style=\"margin-right: 0px;\" width=\"223\" height=\"223\"><br></div></div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "attached-img.jpg",
"body": {
"attachmentId": "ANGjdJ-wHL_YHq_zUYZ4AyCHdstehG_7lhh21SXnzvf_33oECSiFcua3UTEbO2u5gSrEVDS4xvdnQa4e2JFb5olkMbv8rBuAprlADc_99pG_X-kf9gjhCiEIPPpr66S7VkB2Wumh9rBFc0bN6j_8mEjoGEBDAyd7lb38SiY8A6v2TP2o9gaKucYfIB__tiQ4Z1C-pSipyNmToCJfE87TuFp_ukQtDQbrVyG1bEoy2w",
"size": 44988
}
}, {
"partId": "2",
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"filename": "Motivation Letter Sample.docx",
"body": {
"attachmentId": "ANGjdJ9c8IKsgC8Do8iAyDHHGiyiho_zv8eI4BkaW_kvnl7--webl9P01xGWTyCwTRblgClK6dE2JH8P7hNd007ZK-CIU5Stwuc6Mp2v-KHC3slmCfko--JRrU1-EotFJqrnr3gmRw9qZgWlCqKxqiJAo1afE67LtwBSuR_frjCCUPH7RuxdY6mP_WJHSP6XA6i5uyhWaRGpnMOzawbTdq1_ZSKo9mjF__dDOsdrlQ",
"size": 7136
}
}, {
"partId": "0.1",
"mimeType": "image/jpeg",
"filename": "droped-ing.jpg",
"body": {
"attachmentId": "ANGjdJ-hNpJEK_gzrYEsdQODp-Zwe5QoJP5ONsOy5JoSUC1Qk5Po7KgL_AJnMddPRZ1GWpltr-XRtXv3zS5TTUYJxf7BBZyitkMNH9Kv_rnArqXLJyBOqfL1wNqAJPeQrCzUjk6d0ahqAl6ixNyCCgTu-fxvngaBllXU5pTI3_iL6jWXoin6LoQ-a32vhKs319tChOz5GBuTCTov9oZqTtJPvj1yIqLAmUf8vochDQ",
"size": 43716
}
}]
}
without_attachmetnst: {
"text": "Its Body\r\n",
"html": "<div dir=\"ltr\">Its Body<br clear=\"all\"></div>\r\n",
"attachments": []
}
only_inline_attachment: {
"text": "Just-has-inline-attachments-Body\r\n\r\n\r\n[image: Bronze-Floor-Lamp-600x600.jpg]\r\n",
"html": "<div dir=\"ltr\"><br><div class=\"gmail_quote\"><div dir=\"ltr\"><div>Just-has-inline-attachments-Body</div><div><br></div><div><div><br><div><img src=\"cid:ii_jyy2kc7b0\" alt=\"Bronze-Floor-Lamp-600x600.jpg\" style=\"margin-right:0px\" width=\"225\" height=\"225\"><br></div></div></div></div>\r\n</div></div>\r\n",
"attachments": [{
"partId": "1",
"mimeType": "image/jpeg",
"filename": "Bronze-Floor-Lamp-600x600.jpg",
"body": {
"attachmentId": "ANGjdJ-DX3652q5kW-6nNaEQL-q2zvx1Df2FV-GOfx7YfzcY2NnVkM4uMmr058QUaiAX4wsI3LybFtMB6Xaqy6ijsx2RAUg56nESumo2ecDvs-3PUgDshHJgHluwcEmpDIh7H9w6TEHteDDAs9v4jcu5xX-2_GNyeKUVK8BfGY-qYvgwVwWL5fg-TaiZ6SyrtB_w8dSOpQwtw_at25oeGRpmMh7qkLrbZ6yAkfMIXw",
"size": 91648
}
}]
}
I resolved this using a recursive function, in this way obtains all the text of the message without import the level of depth of the Json answer. If need more explication, please tell me.
private List<string> ObtenerTextoMensaje(IList<MessagePart> partes)
{
var listaTextos = new List<string>();
foreach(var elementoParte in partes)
{
if ((elementoParte.MimeType == "text/plain")|| (elementoParte.MimeType == "text/html"))
{
if (elementoParte.Body.Size != 0)
{
listaTextos.Add(decodificarBase64(elementoParte.Body.Data));
}
}
else
{
if(elementoParte.Parts!=null)
listaTextos = ObtenerTextoMensaje(elementoParte.Parts);
}
}
return listaTextos;
}