用Javascript解析Gmail API中的消息

12

我在使用Gmail API获取电子邮件时,解码电子邮件正文内容遇到了严重的问题。我想获取消息内容并将其放入一个div中,我正在使用base64编码器进行解码,但我知道它无法解码其他编码方式编码的电子邮件,但我不确定如何检查电子邮件以决定使用哪个解码器--指明它们是utf-8编码的电子邮件可以被base64解码器成功解码,但不一定能被utf-8解码器解码。

我已经研究了几天的电子邮件解码,我发现这方面有点超出了我的水平。我之前没有做过太多与电子邮件相关的编码工作。以下是我用于获取电子邮件的代码:

gapi.client.load('gmail', 'v1', function() {
var request = gapi.client.gmail.users.messages.list({
  labelIds: ['INBOX']
});
request.execute(function(resp) {
  document.getElementById('email-announcement').innerHTML = '<i>Hello! I am reading your <b>inbox</b> emails.</i><br><br>------<br>';
  var content = document.getElementById("message-list");
  if (resp.messages == null) {
    content.innerHTML = "<b>Your inbox is empty.</b>";
  } else {
    var encodings = 0;
    content.innerHTML = "";
    angular.forEach(resp.messages, function(message) {
      var email = gapi.client.gmail.users.messages.get({
      'id': message.id
      });
      email.execute(function(stuff) {
        if (stuff.payload == null) {
          console.log("Payload null: " + message.id);
        }
        var header = "";
        var sender = "";
        angular.forEach(stuff.payload.headers, function(item) {
          if (item.name == "Subject") {
            header = item.value;
          }
          if (item.name == "From") {
            sender = item.value;
          }
        })
        try {
          var contents = "";
          if (stuff.payload.parts == null) {
            contents = base64.decode(stuff.payload.body.data);
          } else {
            contents = base64.decode(stuff.payload.parts[0].body.data);
          }
          content.innerHTML += '<b>Subject: ' + header + '</b><br>';
          content.innerHTML += '<b>From: ' + sender + '</b><br>';
          content.innerHTML += contents + "<br><br>";
        } catch (err) {
          console.log("Encoding error: " + encodings++);
        }
      })
    })
  }
 });
});

我在进行一些检查和调试,所以代码里有一些多余的console.log和其他一些只用于测试的东西。不过,你可以从这里看到我正在尝试做什么。

从Gmail API获取的电子邮件解码最好的方法是什么?我是否应该尝试将电子邮件放入具有匹配编码内容的charsettype属性的<script>中?我记得charset只能与src属性一起使用,而这里没有src属性。有任何建议吗?

7个回答

25

对于我正在编写的原型应用程序,以下代码对我有效:

var base64 = require('js-base64').Base64;
// js-base64 is working fine for me.

var bodyData = message.payload.body.data;
// Simplified code: you'd need to check for multipart.

base64.decode(bodyData.replace(/-/g, '+').replace(/_/g, '/'));
// If you're going to use a different library other than js-base64,
// you may need to replace some characters before passing it to the decoder.

注意:这些要点没有明确的文件记录,可能是错误的:

  1. users.messages: get API默认返回“解析后的正文内容”。 不管Content-TypeContent-Transfer-Encoding头文件如何,此数据似乎始终以UTF-8和Base64编码。例如,我的代码没有问题解析使用以下标头的电子邮件:Content-Type: text/plain; charset=ISO-2022-JPContent-Transfer-Encoding: 7bit

  2. Base64编码的映射表因实现而异。 Gmail API使用-_作为表的最后两个字符,如RFC 4648中定义的“URL和文件名安全字母表”1。检查您的Base64库是否使用不同的映射表。如果是,则将那些字符替换为库接受的字符,然后将正文传递给解码器。


1文档中有一行支持性语句:“原始”格式以“base64url编码的字符串”的形式返回正文内容。(感谢Eric!)


总体来说非常好,很有帮助,谢谢!请注意,在第二点中提到了用户消息获取API文档:https://developers.google.com/gmail/api/v1/reference/users/messages/get(请参阅format=RAW文档)。但也许还应该进一步扩展... - Eric D
在找到你的可行解决方案之前,我已经尝试了很多选项,谢谢! - walla

5
使用atob在JavaScript中解码消息(参见ref)。要访问您的消息有效载荷,您可以编写一个函数:
var extractField = function(json, fieldName) {
  return json.payload.headers.filter(function(header) {
    return header.name === fieldName;
  })[0].value;
};
var date = extractField(response, "Date");
var subject = extractField(response, "Subject");

参考我的之前的SO问题

var part = message.parts.filter(function(part) {
  return part.mimeType == 'text/html';
});
var html = atob(part.body.data);

如果以上内容无法完全解码,请参考@cgenco在下面的答案中的评论。在这种情况下,执行以下操作:
var html = atob(part.body.data.replace(/-/g, '+').replace(/_/g, '/'));

1
看起来.replace(/-/g, '+').replace(/_/g, '/')仍然需要完成才能正确解码。 - cgenco
@cgenco,感谢分享。您能解释一下为什么没有您的“replace”函数是不够的吗?我很乐意修改我的答案。 - FullStack
根据@ento的答案,Base64编码在不同的实现中存在差异,并且谷歌使用的编码使用“-”代替“+”,使用“_”代替“/”。 - cgenco

2
这里是解决方案: Gmail API - “Users.messages: get”方法的响应消息中,message.payload.body.data被分割成基于64位的数据,并用“-”符号分隔。它不是完整的基于64位的编码文本,而是基于64位编码文本的部分。您必须尝试解码其中的每一部分,或通过联合和替换“-”符号来制作一个单一的字符串。之后,您可以轻松将其解码为人类文本。 您可以在此手动检查每个部分 https://www.base64decode.org

2

我也对此感到很烦恼。通过查看VSCode的一个扩展程序,我找到了解决方案。这个解决方案非常简单:

const body = response.data.payload.body; // the base64 encoded body of a message
 body = Buffer.alloc(
        body.data.length,
        body.data,
        "base64"
      ).toString();  // the decoded message

对于我来说,它有效是因为我正在使用 Gmail API 的 gmail.users.messages.get() 调用。


1

感谢 @ento 的回答。我会进一步解释为什么在解码之前需要将“-”和“_”字符替换为“+”和“/”。

Wiki Base64 变体总结表 显示:

  • RFC 4648 第4节:标准的base64:使用“+”和“/”
  • RFC 4648 第5节:URL安全和文件名安全的标准base64url:使用“-”和“_”

简而言之,Gmail API 使用base64url(urlsafe)格式(“-”和“_”),但 JavaScript atob 函数或其他 JavaScript 库使用base64(标准)格式(“+”和“/”)。

对于 Gmail API,文档说明正文使用 base64url 格式,请参见以下链接:

有关Web atob/btoa标准,请参阅以下链接:


1

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接