PAYATHON 2026

Ruby Gmail API 获取邮件时正文为空,如何读取?

数据小许

结论

邮件正文不一定在 payload.body.data 中。Gmail API 会根据 MIME 结构返回邮件内容:单部分邮件的正文通常在 payload.body.data,多部分邮件的正文可能在 payload.parts 的子部分中。body.data 使用 Base64URL 编码,读取前需要先解码。

raw 字段保存的是原始邮件数据。调用 messages.get 时,指定 format: 'raw' 才会读取该字段;要获取完整解析后的邮件结构,则使用 format: 'full'。因此,puts updated_message.raw 输出为空,不能说明邮件没有正文。

请求完整邮件结构

请求时可以指定 full 格式。下面使用 Google Ruby API 客户端常见的选项哈希写法;具体传参方式请以你使用的客户端版本为准:

message = gmail.get_user_message('me', message_id, format: 'full')

拿到消息后,检查 message.payload.mime_type、message.payload.body.data 和 message.payload.parts。如果消息是 multipart/alternative 或其他 multipart 类型,正文通常在 parts 中,而不是顶层的 body。

遍历 MIME parts 并解码正文

下面的示例递归遍历各部分,优先提取 text/plain,并用 Ruby 的 Base64.urlsafe_decode64 解码 Gmail 返回的数据:

require 'base64'

def collect_text_parts(part, mime_type, result)
  if part.mime_type == mime_type && part.body&.data
    result << Base64.urlsafe_decode64(part.body.data)
  end

  Array(part.parts).each do |child|
    collect_text_parts(child, mime_type, result)
  end
end

messages.each do |listed_message|
  message = gmail.get_user_message('me', listed_message.id, format: 'full')
  payload = message.payload

  plain_text = []
  collect_text_parts(payload, 'text/plain', plain_text)

  if plain_text.empty?
    html = []
    collect_text_parts(payload, 'text/html', html)
    puts html.join("\n")
  else
    puts plain_text.join("\n")
  end
end

如果只需要查看原始 RFC 822 邮件内容,可以请求 raw 格式,再解码 raw 字段:

message = gmail.get_user_message('me', message_id, format: 'raw')
raw_email = Base64.urlsafe_decode64(message.raw)
puts raw_email

raw 包含完整的原始邮件内容,可能包括邮件头、MIME 分隔符和编码后的正文。它不是提取好的纯文本正文。

检查事项

  • 确认请求使用的是 full 还是 raw,并检查返回的 payload.mime_type 和 payload.parts。
  • 如果正文是 HTML,示例会输出 text/html 部分。需要以纯文本展示时,还要自行转换 HTML。
  • 如果某个 MIME part 只有 body.attachment_id,没有 body.data,需要按照 Gmail API 的附件读取方式另行获取该部分内容。

备注:内容仅供参考。