Google Gmail API发送邮件偶现编码异常显示问题排查
问题描述
你使用如下PHP函数调用Google Gmail API实现邮件发送:
function sendMessage($to, $subject, $messageBody, $Cc) { $service = getGoogleGmailService(); $message = new \Google_Service_Gmail_Message(); $rawMessageString = "From: 'My name' <me>\r\n"; $rawMessageString .= "To: <" . $to . ">\r\n"; if (!empty($Cc)) { $rawMessageString .= "Cc: <" . $Cc . ">\r\n"; } $rawMessageString .= 'Subject: =?utf-8?B?' . base64_encode($subject) . "?=\r\n"; $rawMessageString .= "MIME-Version: 1.0\r\n"; $rawMessageString .= "Content-Type: text/html; charset=utf-8\r\n"; $rawMessageString .= 'Content-Transfer-Encoding: base64' . "\r\n\r\n"; $rawMessageString .= $messageBody . "\r\n"; $rawMessage = strtr(base64_encode($rawMessageString), array('+' => '-', '/' => '_')); $message->setRaw($rawMessage); try { $message = $service->users_messages->send('me', $message); return $message->getId(); } catch (Exception $e) { return $e->getMessage(); } }
正常场景下邮件结构符合预期,投递后收件端显示完全正常;但偶发会收到显示异常的邮件,内容呈现类似未完成解码的编码状态,效果对比如下:
异常根因
核心问题是MIME头声明和实际正文编码不匹配:
- 你在邮件头里明确声明了
Content-Transfer-Encoding: base64,等于告诉所有收件客户端“我的邮件正文是base64编码格式,请解码后渲染”,但实际拼接原始邮件内容时,你直接把明文的$messageBody拼了进去,完全没有做base64编码。 - 之所以是偶发不是必现,是因为不同邮件客户端的容错逻辑不同:如果正文全是短长度的纯ASCII字符、没有触发SMTP传输的自动折行规则,很多客户端会忽略编码声明直接渲染明文,看起来就是正常的;一旦正文包含非ASCII字符(中文、emoji、特殊符号)、或者正文长度超过SMTP规定的单行列上限(通用兼容值为76字符)触发传输折行,客户端就会严格按照头声明的base64规则解码明文内容,最终就会渲染出类似未完成解码的乱码状态。
另外代码还存在几个潜在兼容隐患,也可能触发其他场景的显示异常:
- From头的发件人显示名如果包含非ASCII字符,没有做RFC2047编码
- To、Cc字段如果包含收件人昵称,同样没有做合规编码
- 即使补了正文base64编码,如果不按MIME规范每76个字符插入换行,依然可能被传输链路中间的服务器自动折行破坏编码结构
修复方法
把正文部分按照声明的编码规则做base64转换,同时按MIME规范做分块换行即可,替换原来直接拼接$messageBody的代码:
// 替换原来的 $rawMessageString .= $messageBody . "\r\n"; 逻辑 $encodedBody = base64_encode($messageBody); // 按MIME要求每76字符插入换行 $formattedBody = chunk_split($encodedBody, 76, "\r\n"); $rawMessageString .= $formattedBody;
如果需要减少手动拼接MIME头的出错概率,也可以直接用imap_mail_compose扩展或者PHPMailer这类成熟的邮件类库生成合规的原始邮件内容,再传入Gmail API发送。
内容的提问来源于stack exchange,提问作者Dmytro V
相关产品推荐
相关产品推荐



