从Gmail原始内容中解析内联图像

时间:2013-05-28 17:10:01

标签: google-apps-script

Gmail邮件getAttachments功能未返回内联图片 - 请参阅问题2810 https://code.google.com/p/google-apps-script-issues/issues/detail?id=2810

我需要这样做,所以我编写了下面的代码,以消息原始内容解析blob格式的内嵌图像,事先知道消息中的图像cid。

但是,我觉得这个解析在我找到base64图像内容中的第一个和最后一个字符的方式上非常脆弱,不是吗?

有更好的方法吗?

问候,Fausto

var rawc = message.getRawContent();
var b64c1 = rawc.lastIndexOf(cid) + cid.length + 3; // first character in image base64
var b64cn = rawc.substr(b64c1).indexOf("--") - 3; // last character in image base64
var imgb64 = rawc.substring(b64c1, b64c1 + b64cn + 1); // is this fragile or safe enough?
var imgblob = Utilities.newBlob(Utilities.base64Decode(imgb64), "image/jpeg", cid); // decode and blob

2 个答案:

答案 0 :(得分:6)

我已经多次遇到过这个问题了,我想我有一个非常普遍的案例解决方案。获取非嵌入式图像也是一个问题。

我不确定我的解析是否比你的解析更脆弱。最后,我通过抓住以multipart开头的周围行来吮吸'--'的部分。其他一切只是确保我可以使用它而不需要在下次需要时修改代码。我收到了一些电子邮件,这些电子邮件似乎没有跟随\r\n并导致问题:需要注意的事项。

getInlineImages函数将获取消息的原始内容并返回一个对象数组。每个对象都有img标签的src和与图像一起的blob。如果您只想要内嵌图像,则可以选择忽略任何不以“cid”开头的内容。

getBlobFromMessage函数将获取消息的原始内容和img标记的src(包括'cid')并返回关联的blob。

您可以看到评论here的代码。

function getInlineImages(rawContent) {
  var url = /^https?:\/\//, cid = /^cid:/;
  var imgtags = rawContent.match(/<img.*?>(.*?<\/img>)?/gi);
  return imgtags ? imgtags.map(function(imgTag) {
    var img = {src: Xml.parse(imgTag,true).html.body.img.src};
    img.blob = url.test(img.src) ? UrlFetchApp.fetch(img.src).getBlob()
             : cid.test(img.src) ? getBlobFromMessage(rawContent,img.src)
             : null;
    return img;
  }) : [];
}

function getBlobFromMessage(rawContent,src) {
  var cidIndex = src.search(/cid:/i);
  if(cidIndex === -1) throw Utilities.formatString("Did not find cid: prefix for inline refenece: %s", src)

  var itemId = src.substr(cidIndex + 4);
  var contentIdIndex = rawContent.search("Content-ID:.*?" + itemId);
  if(contentIdIndex === -1) throw Utilities.formatString("Item with ID %s not found.",src);

  var previousBoundaryIndex = rawContent.lastIndexOf("\r\n--",contentIdIndex);
  var nextBoundaryIndex = rawContent.indexOf("\r\n--",previousBoundaryIndex+1);
  var part = rawContent.substring(previousBoundaryIndex,nextBoundaryIndex);

  var contentTransferEncodingLine = part.match(/Content-Transfer-Encoding:.*?\r\n/i)[0];
  var encoding = contentTransferEncodingLine.split(":")[1].trim();
  if(encoding != "base64") throw Utilities.formatString("Unhandled encoding type: %s",encoding);

  var contentTypeLine = part.match(/Content-Type:.*?\r\n/i)[0];
  var contentType = contentTypeLine.split(":")[1].split(";")[0].trim();

  var startOfBlob = part.indexOf("\r\n\r\n");
  var blobText = part.substring(startOfBlob).replace("\r\n",""); 
  return Utilities.newBlob(Utilities.base64Decode(blobText),contentType,itemId);
}

答案 1 :(得分:0)

针对此问题的最新方法。

问题

例如,这是使用.getBody()

检索的电子邮件正文
<div dir="ltr"><div><img src="?view=att&amp;th=1401f70d4881e07f&amp;attid=0.3&amp;disp=emb&amp;realattid=ii_1401f6fc7824ebe1&amp;zw&amp;atsh=1" alt="Inline image 4" width="200" height="180"><br></div><div><br></div><img src="?view=att&amp;th=1401f70d4881e07f&amp;attid=0.2&amp;disp=emb&amp;realattid=ii_1401f6e6c1d46c4b&amp;zw&amp;atsh=1" alt="Inline image 2" width="200" height="65"><div><br></div><div>
jtykuykyu</div><div><br></div><div><img src="?view=att&amp;th=1401f70d4881e07f&amp;attid=0.1&amp;disp=emb&amp;realattid=ii_1401f6e9df3a4b1c&amp;zw&amp;atsh=1" alt="Inline image 3" width="200" height="82"><br><div><br></div><div><br></div></div></div>

以下是电子邮件的附件列表(其中包括我们的内联图片):

[13-07-30 08:28:08:378 CEST]截图2013-07-12 at 1.54.31 PM.png

[13-07-30 08:28:08:379 CEST]截图2013-07-23 at 5.38.51 PM.png

[13-07-30 08:28:08:380 CEST]截图2013-07-25 at 9.05.15 AM.png

[13-07-30 08:28:08:381 CEST] test2.png

正如您所看到的,这些图片的名称与img标签中提供的信息之间没有任何关联,因此没有安全的方法来重建只包含这些信息的正确电子邮件。

解决方案

如何解决?我们可以使用方法.getRawContent()来获取实际的电子邮件并解析它以获取我们需要的信息。具体来说,这种方法为我们提供了一个附件名称和“realattid”之间的关系。可在电子邮件正文中找到:

内容类型:image / png; name =&#34; Screen Shot 2013-07-25 at 9.05.15 AM.png&#34;

Content-Transfer-Encoding:base64

Content-ID:

X-Attachment-Id:ii_1401f6e9df3a4b1c

代码段

这是一个代码段:

- 检查身体和身体电子邮件的附件

- 获取正文中的所有img标记,并查看哪些标记链接到电子邮件中的附件

- 获得&#39; realattid&#39;每个图像的使用.getRawContent()来链接这个&#39; realattid&#39;正确的附件

- 更换img标签以正确将其链接到正确的附件

- 表示此附件不再是简单附件,而是内嵌图像

- 完成所有操作后,您将获得发送此电子邮件副本所需的所有数据,并显示正确的内嵌图像。

 //////////////////////////////////////////////////////////////////////////////
  // Get inline images and make sure they stay as inline images
  //////////////////////////////////////////////////////////////////////////////
  var emailTemplate = selectedTemplate.getBody();
  var rawContent = selectedTemplate.getRawContent();
  var attachments = selectedTemplate.getAttachments();

  var regMessageId = new RegExp(selectedTemplate.getId(), "g");
  if (emailTemplate.match(regMessageId) != null) {
    var inlineImages = {};
    var nbrOfImg = emailTemplate.match(regMessageId).length;
    var imgVars = emailTemplate.match(/<img[^>]+>/g);
    var imgToReplace = [];
    if(imgVars != null){
      for (var i = 0; i < imgVars.length; i++) {
        if (imgVars[i].search(regMessageId) != -1) {
          var id = imgVars[i].match(/realattid=([^&]+)&/);
          if (id != null) {
            var temp = rawContent.split(id[1])[1];
            temp = temp.substr(temp.lastIndexOf('Content-Type'));
            var imgTitle = temp.match(/name="([^"]+)"/);
            if (imgTitle != null) imgToReplace.push([imgTitle[1], imgVars[i], id[1]]);
          }
        }
      }
    }
    for (var i = 0; i < imgToReplace.length; i++) {
      for (var j = 0; j < attachments.length; j++) {
        if(attachments[j].getName() == imgToReplace[i][0]) {
          inlineImages[imgToReplace[i][2]] = attachments[j].copyBlob();
          attachments.splice(j, 1);
          var newImg = imgToReplace[i][1].replace(/src="[^\"]+\"/, "src=\"cid:" + imgToReplace[i][2] + "\"");
          emailTemplate = emailTemplate.replace(imgToReplace[i][1], newImg);
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////
  var message = {
    htmlBody: emailTemplate,
    subject: selectedTemplate.getSubject(),
    attachments: attachments,
    inlineImages: inlineImages
  }