3

I have a 'message' object, the response from the Gmail API, in my frontend and adapting this very useful Javascript gist I've parsed the message as well.

function indexHeaders(headers) {
        if (!headers) {
          return {};
        } else {
          return headers.reduce(function (result, header) {
            result[header.name.toLowerCase()] = header.value;
            return result;
          }, {});
        }
      }


function urlB64Decode(string) {
  encodedBody = string 
  encodedBody = encodedBody.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '');
  return decodeURIComponent(escape(window.atob(encodedBody)));
}


function parseMessage(response) {
  var result = {
    id: response.id,
    threadId: response.threadId,
    labelIds: response.labelIds,
    snippet: response.snippet,
    historyId: response.historyId
  };
  if (response.internalDate) {
    result.internalDate = parseInt(response.internalDate);
  }

  var payload = response.payload;
  if (!payload) {
    return result;
  }

  var headers = indexHeaders(payload.headers);
  result.headers = headers;

  var parts = [payload];
  var firstPartProcessed = false;

  while (parts.length !== 0) {
    var part = parts.shift();
    if (part.parts) {
      parts = parts.concat(part.parts);
    }
    if (firstPartProcessed) {
      headers = indexHeaders(part.headers);
    }

    if (!part.body) {
      continue;
    }

    var isHtml = part.mimeType && part.mimeType.indexOf('text/html') !== -1;
    var isPlain = part.mimeType && part.mimeType.indexOf('text/plain') !== -1;
    var isAttachment = headers['content-disposition'] && headers['content-disposition'].indexOf('attachment') !== -1;
    var isInline = headers['content-disposition'] && headers['content-disposition'].indexOf('inline') !== -1;

    if (isHtml && !isAttachment) {
      result.textHtml = urlB64Decode(part.body.data);
    } else if (isPlain && !isAttachment) {
      result.textPlain = urlB64Decode(part.body.data);
    } else if (isAttachment) {
      var body = part.body;
      if(!result.attachments) {
        result.attachments = [];
      }
      result.attachments.push({
        filename: part.filename,
        mimeType: part.mimeType,
        size: body.size,
        attachmentId: body.attachmentId,
        headers: indexHeaders(part.headers)
      });
    } else if (isInline) {
    var body = part.body;
    if(!result.inline) {
      result.inline = [];
    }
    result.inline.push({
      filename: part.filename,
      mimeType: part.mimeType,
      size: body.size,
      attachmentId: body.attachmentId,
      headers: indexHeaders(part.headers)
    });
  }

    firstPartProcessed = true;
  }

  return result;
};

So, after parsing the response, it's now in this format:

{ 
  //   id: '{MESSAGE_ID}',
  //   threadId: '{THREAD_ID}',
  //   labelIds: [ 'SENT', 'INBOX', 'UNREAD' ],
  //   snippet: 'This is one cool message, buddy.',
  //   historyId: '701725',
  //   internalDate: 1451995756000,
  //   attachments: [{ 
  //     filename: 'example.jpg',
  //     mimeType: 'image/jpeg',
  //     size: 100446,
  //     attachmentId: '{ATTACHMENT_ID}',
  //     headers: {
  //       'content-type': 'image/jpeg; name="example.jpg"',
  //       'content-description': 'example.jpg',
  //       'content-transfer-encoding': 'base64',
  //       'content-id': '...',
  //       ...
  //     }
  //   }],
  //   inline: [{ 
  //     filename: 'example.png',
  //     mimeType: 'image/png',
  //     size: 5551,
  //     attachmentId: '{ATTACHMENT_ID}',
  //     headers: {
  //       'content-type': 'image/jpeg; name="example.png"',
  //       'content-description': 'example.png',
  //       'content-transfer-encoding': 'base64',
  //       'content-id': '...',
  //       ...
  //     }
  //   }],
  //   headers: {
  //     subject: 'Example subject',
  //     from: 'Example Name <example@gmail.com>',
  //     to: '<foo@gmail.com>, Foo Bar <fooBar@gmail.com>',
  //     ...
  //   },
  //   textPlain: '<div dir="ltr">hey! <div><br></div><div><div><img src="cid:ii_k0l2i10d0" alt="Image_Name 2019-09-11 at 20.47.16.jpeg" width="452" height="339"><br></div></div></div>',
  //   textHtml: '<div dir="ltr">hey! <div><br></div><div><div><img src="cid:ii_k0l2i10d0" alt="Image_Name 2019-09-11 at 20.47.16.jpeg" width="452" height="339"><br></div></div></div>'
  // }

I'm having difficulty rendering the actual inline image / attachment in html though. If I use the 'textHtml' that comes through in the parsedMessage, it renders as something like <img src='cid:ii_k0l2i10d0'> in the html, which doesn't display the image.

This Stackoverflow answer was helpful but I need help with the last part. I have the attachmentID now. It says "Get the attachment from the Gmail API and replace the cid with the base64-data" which is the part I'm struggling with. How do I do that? -- Do I have to make another call to the Gmail API for this? Or am I going wrong somewhere in the decoding?

Thanks so much for any help!

cydb
  • 67
  • 1
  • 9
  • Is [this answer](https://stackoverflow.com/a/37688529/11581830) of assistance to you? – AMolina Sep 16 '19 at 14:05
  • Thanks for the comment @AMolina! I kind of understand this part from the answer you linked: "Here, we have two cids, nlFlag and frFlag, each associated with a file Blob. File blobs can be obtained in many ways; here we've used UrlFetchApp.fetch() to get the binary images from WikiMedia, then converted them to blobs and set the name of the blob for attaching." In my case, of course, I'm trying to do the opposite -- decode it. Still stumped by that! – cydb Sep 16 '19 at 14:58

1 Answers1

1

I finally got it working -- so the answer is, yes, I did have to make another call to the API to get the attachments data. That returns an attachment resource that looks like this:

{
  "attachmentId": string,
  "size": integer,
  "data": bytes
}

Once I get the response:

// construct the new src with the data from the response 
newSrc = "data:" + "image/jpeg" + ";" + "base64" + "," + response.data.replace(/-/g, `+`).replace(/_/g, `/`)
// replace the src of the image with the new src 
thisImg.attr({"src":newSrc});
cydb
  • 67
  • 1
  • 9