如何正确地对 Gmail API 的 MIMEText 进行 base64 编码?

How do I properly base64 encode a MIMEText for Gmail API?

来自Gmail API's developer guide

The following code sample demonstrates creating a MIME message, encoding to a base64url string, and assigning it to the raw field of the Message resource:

def create_message(sender, to, subject, message_text):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string())}

但是如果我做类似

from email.MIMEText import MIMEText
import base64
message = MIMEText('This is a test')
message['to'] = 'test@gmail.com'
message['from'] = 'tester@gmail.com'
message['subject'] = 'Test'
body = {'raw': base64.urlsafe_b64encode(message.as_string())}

我明白了 TypeError: a bytes-like object is required, not 'str' 如果我这样做:

body = {'raw': base64.urlsafe_b64encode(message.as_bytes())}
message = (service.users().messages().send(userId='me', body=body)
               .execute())

我得到以下关于某些二进制文件未 json 可序列化的回溯:

~/env/lib/python3.5/site-packages/googleapiclient/discovery.py in method(self, **kwargs)
    792     headers = {}
    793     headers, params, query, body = model.request(headers,
--> 794         actual_path_params, actual_query_params, body_value)
    795 
    796     expanded_url = uritemplate.expand(pathUrl, params)

~/env/lib/python3.5/site-packages/googleapiclient/model.py in request(self, headers, path_params, query_params, body_value)
    149     if body_value is not None:
    150       headers['content-type'] = self.content_type
--> 151       body_value = self.serialize(body_value)
    152     self._log_request(headers, path_params, query, body_value)
    153     return (headers, path_params, query, body_value)

~/env/lib/python3.5/site-packages/googleapiclient/model.py in serialize(self, body_value)
    258         self._data_wrapper):
    259       body_value = {'data': body_value}
--> 260     return json.dumps(body_value)
    261 
    262   def deserialize(self, content):

/usr/lib/python3.5/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    228         cls is None and indent is None and separators is None and
    229         default is None and not sort_keys and not kw):
--> 230         return _default_encoder.encode(obj)
    231     if cls is None:
    232         cls = JSONEncoder

/usr/lib/python3.5/json/encoder.py in encode(self, o)
    196         # exceptions aren't as detailed.  The list call should be roughly
    197         # equivalent to the PySequence_Fast that ''.join() would do.
--> 198         chunks = self.iterencode(o, _one_shot=True)
    199         if not isinstance(chunks, (list, tuple)):
    200             chunks = list(chunks)

/usr/lib/python3.5/json/encoder.py in iterencode(self, o, _one_shot)
    254                 self.key_separator, self.item_separator, self.sort_keys,
    255                 self.skipkeys, _one_shot)
--> 256         return _iterencode(o, 0)
    257 
    258 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,

/usr/lib/python3.5/json/encoder.py in default(self, o)
    177 
    178         """
--> 179         raise TypeError(repr(o) + " is not JSON serializable")
    180 
    181     def encode(self, o):

TypeError: b'Q29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PSJ1cy1hc2NpaSIKTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogN2JpdApGcm9tOiBUZXN0IFB5dGhvbgpUbzogcmFwaGFlbC5hLmR1bWFzQGdtYWlsLmNvbQpTdWJqZWN0OiBQbGVhc2Ugc3RheSBjYWxtLCB0aGlzIGlzIGEgdGVzdAoKVGhpcyBpcyBhIHRlc3Q=' is not JSON serializable

尝试:

b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
b64_string = b64_bytes.decode()
body = {'raw': b64_string}

base64.urlsafe_b64encode returns 字节对象(参见 docs),因此您需要在将其序列化为 JSON.[=13= 之前将输出转换为字符串]