使用 Gmail API 发送的邮件中缺少附件,但仅适用于收件人

Posted

技术标签:

【中文标题】使用 Gmail API 发送的邮件中缺少附件,但仅适用于收件人【英文标题】:Attachment missing from message sent with Gmail API but only for recipient 【发布时间】:2017-06-22 14:58:56 【问题描述】:

javascript 中使用 Gmail API 发送带有 html 正文和约 100KB PDF 附件的邮件时,附件会正确显示为附加到发件人的 Gmail 已发送文件夹中的邮件,但不会显示在收件人的邮件中.

API 调用是 POST 到:

https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=media

发送到 API 的请求正文是:


  "headers": 
    "Authorization": "Bearer authToken-removedForThisPost"
  ,
  "method": "POST",
  "contentType": "message/rfc822",
  "contentLength": 134044,
  "payload": "exampleBelow",
  "muteHttpExceptions": true

这是有效载荷的样子:

MIME-Version: 1.0
To: =?utf-8?B?TWlrZSBD?=<recipient@test.com>
CC: =?utf-8?B?TWlrZSBD?=<secondrecipient@gmail.com>
BCC: =?utf-8?B??=<bccrecipient@test.com>
From: =?utf-8?B?TWlrZSBxWXsd2lr?=<sender@test.com>
Subject: =?utf-8?B?subjectLine-removedForThisPost?=
Content-Type: multipart/alternative; boundary=__boundary__

--__boundary__
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64

base64EncodedStringHere-removedForThisPost

--__boundary__
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64

base64EncodedStringHere-removedForThisPost

--__boundary__
Content-Type: application/pdf; name="File Name.pdf"
Content-Disposition: attachment; filename="File Name.pdf"
Content-Transfer-Encoding: base64

base64EncodedStringHere-removedForThisPost

--__boundary__--

注意:Gmail API Uploading Attachments documentation 声明在上传简单附件(小于 5MB)时需要Content-Length。我这样做是为了让我的代码生成 PDF 附件总字节数的整数值。但是,我注意到 Content-Length 不包含在有效负载中。

我尝试将邮件的 multipart/alternative Content-Type 更改为 multipart/mixed - 这使得 PDF 附件正确附加到收件人的邮件中,但邮件的 HTML 正文呈现为纯文本( HTML 标记已显示),还有一个名为 noname.html 的附加附件,其中包含呈现为 HTML 的 HTML 内容。

我需要使收件人邮件中的电子邮件具有 HTML 呈现的正文和 PDF 附件。

更新:我上传了 here 的原始电子邮件示例。 已发送消息在左侧,已接收消息在右侧。

【问题讨论】:

您是否尝试过引用边界 (boundary="__boundary__") 并使用最终边界 (--__boundary__--)?试试like this 看看它是否有效。 刚刚意识到我的payload确实包含--__boundary__--的最终边界,但是当我将它粘贴到这里时它被切断了,因为console.log消息由于超长的附件base64字符串而被截断。至于双引号 - 我将它们添加到第一行 Content-Type: ,但它根本没有改变行为 - 无论有没有它们都一样。 只是为了检查,当从 UI 发送此消息时,是否收到了带有相同附件的消息?此外,所有收件人都发生这种情况吗?还是只针对特定的域/用户? 是的,当在 Gmail UI 中发送带有附件的邮件时,收件人会正确接收。我还测试了多个不同的附件,以排除文件存在问题的可能性。而且我已经在不同域中对几个不同的收件人进行了测试,所有收件人都缺少附件。 【参考方案1】:

只需替换:

Content-Type: multipart/alternative; boundary=__boundary__

为:

Content-Type: multipart/mixed; boundary=__boundary__

这是我用 JS 写的完整函数

函数 createMimeMessage_(msg)

var nl = "\n"; var 边界 = "ctrlq_dot_org";

var mimeBody = [

"MIME-Version: 1.0",
"To: "      + msg.to.email,//+ encode_(msg.to.name) + "<" + msg.to.email + ">",
"Cc: "      + msg.cc.email,
"Bcc: "      + msg.bcc.email,
"From: "    + msg.from.email,//+ encode_(msg.from.name) + "<" + msg.from.email + ">",
"Subject: " + encode_(msg.subject), // takes care of accented characters
"In-Reply-To: " + (msg.reply_to || ""),
"References: " + (msg.reply_to || ""),

"Content-Type: multipart/mixed; boundary=" + boundary + nl,
"--" + boundary,

// "Content-Type: text/plain; charset=UTF-8",
// "Content-Transfer-Encoding: 7bit",
// "Content-Disposition: inline" + nl,
// msg.body.text + nl,
// "--" + boundary,

"Content-Type: text/html; charset=UTF-8",
"Content-Transfer-Encoding: base64" + nl,
new Buffer(msg.body.text).toString('base64') + nl,

];

for (var i = 0; i

var attachment = [
  "--" + boundary,
  "Content-Type: " + msg.files[i].mimeType + '; name="' + msg.files[i].fileName + '"',
  'Content-Disposition: attachment; filename="' + msg.files[i].fileName + '"',
  "Content-Transfer-Encoding: base64" + nl,
  msg.files[i].bytes
];

mimeBody.push(attachment.join(nl));

mimeBody.push("--" + 边界 + "--"); //console.log(mimeBody);

返回 mimeBody.join(nl);

【讨论】:

我在原始问题中提到我已经尝试过:这使得 PDF 附件正确附加到收件人的邮件中,但邮件的 HTML 正文呈现为纯文本(显示 HTML 标记),还有一个名为 noname.html 的附加附件,其中包括呈现为 HTML 的 HTML 内容。 嘿,感谢您包含整个功能。但是,您似乎只是从 Amit Agarwal 的页面 here 复制了大部分(如果不是全部),这是我最初从中获取大部分代码的地方,因此它几乎完全匹配。 没错,我是从那个网站复制的。但我做了一些改变,让它满足我的需要。【参考方案2】:

您的问题分为两部分:

    如何将附件发送给收件人? 如何同时包含附件和纯文本替代 HTML?

Tiger 开发人员(multipart/alternativemultipart/mixed)部分回答了这个问题。正如您所指出的,问题在于简单地这样做会阻止您获得 alternative 纯文本。这是因为您要删除 multipart/alternative,其具体作用是提供替代方案。

您需要做的是创建第二个边界,然后将纯文本和 HTML 部分组合在一起。看看这个例子,同样来自CTRLQ,并注意我包含的altBoundary

function createMimeMessage_(msg) 

  var nl = "\n";
  var boundary = "__ctrlq_dot_org__";
  var altBoundary = "__alt_ctrlq_dot_org__";

  var mimeBody = [

    "MIME-Version: 1.0",
    "To: "      + encode_(msg.to.name) + "<" + msg.to.email + ">",
    "From: "    + encode_(msg.from.name) + "<" + msg.from.email + ">",
    "Subject: " + encode_(msg.subject), // takes care of accented characters

    "Content-Type: multipart/mixed; boundary=" + boundary + nl,
    "--" + boundary,

    "Content-Type: multipart/alternative; boundary=" + altBoundary + nl,
    "--" + altBoundary,

    "Content-Type: text/plain; charset=UTF-8",
    "Content-Transfer-Encoding: base64" + nl,
    Utilities.base64Encode(msg.body.text, Utilities.Charset.UTF_8) + nl,
    "--" + altBoundary,

    "Content-Type: text/html; charset=UTF-8",
    "Content-Transfer-Encoding: base64" + nl,
    Utilities.base64Encode(msg.body.html, Utilities.Charset.UTF_8) + nl,

    "--" + altBoundary + "--"

  ];

  for (var i = 0; i < msg.files.length; i++) 

    var attachment = [
      "--" + boundary,
      "Content-Type: " + msg.files[i].mimeType + '; name="' + msg.files[i].fileName + '"',
      'Content-Disposition: attachment; filename="' + msg.files[i].fileName + '"',
      "Content-Transfer-Encoding: base64" + nl,
      msg.files[i].bytes
    ];

    mimeBody.push(attachment.join(nl));

  

  mimeBody.push("--" + boundary + "--");

  return mimeBody.join(nl);


【讨论】:

以上是关于使用 Gmail API 发送的邮件中缺少附件,但仅适用于收件人的主要内容,如果未能解决你的问题,请参考以下文章

GMAIL API 在 C# 中发送带附件的电子邮件

在 golang 中使用 gmail API 发送带附件的电子邮件

在 Javascript 中使用 GMAIL API 发送带有附件文件(超过 10 MB)的电子邮件

用于发送带有附件的电子邮件的 GMAIL API

如何在 ASP.NET 中使用具有 HTML 正文 + 附件的 GMAIL API 发送电子邮件

电子邮件正文在文件中发送 [Gmail API, Node.js]