简体   繁体   English

使用 python 3 和 Gmail API 发送带有附件的电子邮件,我最终遇到文件损坏或 ConnectionAbortedError

[英]Using python 3 and Gmail API to send emails with attachments, I end up with either corrupted files or ConnectionAbortedError

I am using the Gmail API in Python 3 to send emails with attachments, based on their example code我正在使用 Python 3 中的 Gmail API 根据他们的示例代码发送带有附件的电子邮件

I've got the following to create the message:我有以下内容来创建消息:

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

And the following to send:并发送以下内容:

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)

When I send a mail created like this (I need to send pdf's, but tried with a zip as well with the same results) it works but the files are corrupted.当我发送这样创建的邮件(我需要发送 pdf 文件,但也尝试使用 zip 文件并获得相同的结果)时,它可以工作,但文件已损坏。 I'm assuming this happens during the base64 encoding.我假设这发生在 base64 编码期间。

I saw in another post that adding encoders.encode_base64(msg) (just above/below filename = os.path.basename(file) in my case) solves the issue, however when I add that line I get: ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine我在另一篇文章中看到添加encoders.encode_base64(msg) (在我的例子中就在filename = os.path.basename(file)上方/下方)解决了这个问题,但是当我添加那一行时,我得到: ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine

Apparently it does that when it doesn't like a file?显然,当它不喜欢文件时会这样做吗?

Any idea what I'm doing wrong?知道我做错了什么吗?

The confusion with the correct encoding is due to the change from Python 2 to Python 3与正确编码的混淆是由于从 Python 2 更改为 Python 3

The encoding procedure described in the Google documentation for sending emails is based on Python 2. I assume you are using Python 3. Google 文档中描述的用于发送电子邮件的编码过程基于 Python 2。我假设您使用的是 Python 3。

To adapt the guide to the new Python version two modifications need to be implemented.为了使指南适应新的 Python 版本,需要进行两个修改。

  1. Modify return {'raw': base64.urlsafe_b64encode(message.as_string())} to return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()} .修改return {'raw': base64.urlsafe_b64encode(message.as_string())} return {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()} You have done this already.你已经这样做了。
  2. After msg.set_payload(contents) request you need to add an additional line encoders.encode_base64(msg) .msg.set_payload(contents)请求之后,您需要添加额外的行msg.set_payload(contents) encoders.encode_base64(msg) This is what you were missing.这就是你所缺少的。

Now that you added it, mind that encoders is a module of the package email .现在您添加了它,请注意encoders是包email一个模块。 If not already done, you need to add to your code from email import encoders如果尚未完成,您需要from email import encoders添加到您的代码中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM