Используя python 3 и Gmail API для отправки писем с вложениями, я получаю либо поврежденные файлы, либо ConnectionAbortedError - PullRequest
0 голосов
/ 27 января 2020

Я использую Gmail API в Python 3 для отправки электронных писем с вложениями, основываясь на их примере кода

У меня есть следующее для создания сообщения:

def create_message_with_attachment(
    sender, to, subject, message_text, files):
  """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.
    file: The path to the file to be attached.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEMultipart()
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject

  msg = MIMEText(message_text)
  message.attach(msg)

  for file in files:

    content_type, encoding = mimetypes.guess_type(file)

    if content_type is None or encoding is not None:
      content_type = 'application/octet-stream'
    main_type, sub_type = content_type.split('/', 1)
    if main_type == 'text':
      fp = open(file, 'rb')
      msg = MIMEText(fp.read(), _subtype=sub_type)
      fp.close()
    elif main_type == 'image':
      fp = open(file, 'rb')
      msg = MIMEImage(fp.read(), _subtype=sub_type)
      fp.close()
    elif main_type == 'audio':
      fp = open(file, 'rb')
      msg = MIMEAudio(fp.read(), _subtype=sub_type)
      fp.close()
    else:
      fp = open(file, 'rb')
      msg = MIMEBase(main_type, sub_type)
      msg.set_payload(fp.read())
      fp.close()
    filename = os.path.basename(file)
    msg.add_header('Content-Disposition', 'attachment', filename=filename)
    message.attach(msg)

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

И следующее для отправки:

def send_message(service, user_id, message):
    """Send an email message.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

  Returns:
    Sent Message.
    """
    try:
        message = (service.users().messages().send(userId=user_id, body=message).execute())
        print ('Sent! Message Id: %s' % message['id'])
        return message
    except httplib2.HttpLib2Error as error:
        return None
        print ('An error occurred: %s' % error)

Когда я отправляю почту, созданную таким образом (мне нужно отправить pdf-файлы, но пробовал также с zip-файлом с теми же результатами), это работает, но файлы повреждены , Я предполагаю, что это происходит во время кодирования base64.

Я видел в другом посте , что добавление encoders.encode_base64(msg) (чуть выше / ниже filename = os.path.basename(file) в моем случае) решает проблему, однако когда я добавляю эту строку, я получаю: ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine

Видимо, это происходит, когда ему не нравится файл?

Есть идеи, что я делаю неправильно?

1 Ответ

0 голосов
/ 28 января 2020

Путаница с правильной кодировкой вызвана изменением с Python 2 на Python 3

Процедура кодирования, описанная в документации Google для отправки электронных писем , основана на Python 2. Я предполагаю, что вы используете Python 3.

Чтобы адаптировать руководство к новой версии Python, необходимо выполнить две модификации.

  1. Изменить return {'raw': base64.urlsafe_b64encode(message.as_string())} до return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}. Вы уже сделали это.
  2. После msg.set_payload(contents) запроса вам нужно добавить дополнительную строку encoders.encode_base64(msg). Это то, что вы пропустили.

Теперь, когда вы добавили его, помните, что encoders является модулем пакета email. Если это еще не сделано, вам необходимо добавить в свой код from email import encoders

...