如何使用 SMTP 发送附件?

Posted

技术标签:

【中文标题】如何使用 SMTP 发送附件?【英文标题】:How do I send attachments using SMTP? 【发布时间】:2010-12-30 06:43:00 【问题描述】:

我想编写一个使用 Python 的smtplib 发送电子邮件的程序。我搜索了文档和 RFC,但找不到与附件相关的任何内容。因此,我确信我错过了一些更高层次的概念。有人能告诉我附件在 SMTP 中是如何工作的吗?

【问题讨论】:

要明确一点,SMTP 中根本没有任何东西可以处理这个问题,它完全是通过将要发送的文档构造为 MIME 文档来处理的。***上关于 MIME 的文章似乎很好地涵盖了基本知识。 包含直接指向 Python 文档“电子邮件示例”部分的链接将使任何答案都完整:docs.python.org/library/email-examples.html 我相信@PeterHansen 的链接已移至docs.python.org/3/library/email.examples.html 【参考方案1】:

这是一个带有 PDF 附件、文本“正文”并通过 Gmail 发送的邮件示例。

# Import smtplib for the actual sending function
import smtplib

# For guessing MIME type
import mimetypes

# Import the email modules we'll need
import email
import email.mime.application

# Create a text/plain message
msg = email.mime.Multipart.MIMEMultipart()
msg['Subject'] = 'Greetings'
msg['From'] = 'xyz@gmail.com'
msg['To'] = 'abc@gmail.com'

# The main body is just another attachment
body = email.mime.Text.MIMEText("""Hello, how are you? I am fine.
This is a rather nice letter, don't you think?""")
msg.attach(body)

# PDF attachment
filename='simple-table.pdf'
fp=open(filename,'rb')
att = email.mime.application.MIMEApplication(fp.read(),_subtype="pdf")
fp.close()
att.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(att)

# send via Gmail server
# NOTE: my ISP, Centurylink, seems to be automatically rewriting
# port 25 packets to be port 587 and it is trashing port 587 packets.
# So, I use the default port 25, but I authenticate. 
s = smtplib.SMTP('smtp.gmail.com')
s.starttls()
s.login('xyz@gmail.com','xyzpassword')
s.sendmail('xyz@gmail.com',['xyz@gmail.com'], msg.as_string())
s.quit()

【讨论】:

这也解决了我通过电子邮件发送 excel 文件的问题,这太棒了,因为它让我无法使用 os.system 调用 Ruby!谢谢凯文! 在使用 Python xlwt 模块创建 .xls 文件后,此解决方案也适用于我。我没有通过 Gmail 发送,而是使用了我公司的邮件服务器。谢谢,+1 这个方法确实有效,而且更干净! 在这里工作!谢谢凯文 添加标题(在 python2.7.9 中)没有正确命名文件。我的解决方法是将行更改为:att.add_header('Content-Disposition', 'attachment; filename=%s' % filename)【参考方案2】:

这是我从我们所做的工作应用程序中截取的一个示例。它会创建一个带有 Excel 附件的 HTML 电子邮件。

  import smtplib,email,email.encoders,email.mime.text,email.mime.base

  smtpserver = 'localhost'
  to = ['email@somewhere.com']
  fromAddr = 'automated@hi.com'
  subject = "my subject"

  # create html email
  html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
  html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
  html +='<body style="font-size:12px;font-family:Verdana"><p>...</p>'
  html += "</body></html>"
  emailMsg = email.MIMEMultipart.MIMEMultipart('alternative')
  emailMsg['Subject'] = subject
  emailMsg['From'] = fromAddr
  emailMsg['To'] = ', '.join(to)
  emailMsg['Cc'] = ", ".join(cc)
  emailMsg.attach(email.mime.text.MIMEText(html,'html'))

  # now attach the file
  fileMsg = email.mime.base.MIMEBase('application','vnd.ms-excel')
  fileMsg.set_payload(file('exelFile.xls').read())
  email.encoders.encode_base64(fileMsg)
  fileMsg.add_header('Content-Disposition','attachment;filename=anExcelFile.xls')
  emailMsg.attach(fileMsg)

  # send email
  server = smtplib.SMTP(smtpserver)
  server.sendmail(fromAddr,to,emailMsg.as_string())
  server.quit()

【讨论】:

多部分子类型应该是“混合”而不是“替代”,否则在某些电子邮件客户端中您将看不到附件。【参考方案3】:

您要检查的是email 模块。它允许您构建符合MIME 的消息,然后使用 smtplib 发送这些消息。

【讨论】:

【参考方案4】:

嗯,附件没有以任何特殊方式处理,它们只是消息对象树的“只是”叶子。您可以在email python 包的文档的this 部分找到有关符合 MIME 的消息的任何问题的答案。

一般来说,任何类型的附件(阅读:原始二进制数据)都可以使用base64来表示 (或类似)Content-Transfer-Encoding.

【讨论】:

【参考方案5】:

以下是发送带有 zip 文件附件和 utf-8 编码主题+正文的电子邮件的方法。

由于缺乏针对此特定案例的文档和示例,因此很难弄清楚这一点。

回复中的非 ascii 字符需要使用例如 ISO-8859-1 进行编码。可能有一个函数可以做到这一点。

提示:给自己发送一封电子邮件,保存并检查内容以找出如何在 Python 中执行相同的操作。

这是 Python 3 的代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:set ts=4 sw=4 et:

from os.path import basename
from smtplib import SMTP
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.utils import parseaddr, formataddr
from base64 import encodebytes

def send_email(recipients=["somebody@somewhere.xyz"],
         subject="Test subject æøå",
         body="Test body æøå",
         zipfiles=[],
         server="smtp.somewhere.xyz",
         username="bob",
         password="password123",
         sender="Bob <bob@somewhere.xyz>",
         replyto="=?ISO-8859-1?Q?M=F8=F8=F8?= <bob@somewhere.xyz>"): #: bool
    """Sends an e-mail"""
    to = ",".join(recipients)
    charset = "utf-8"
    # Testing if body can be encoded with the charset
    try:
        body.encode(charset)
    except UnicodeEncodeError:
        print("Could not encode " + body + " as " + charset + ".")
        return False

    # Split real name (which is optional) and email address parts
    sender_name, sender_addr = parseaddr(sender)
    replyto_name, replyto_addr = parseaddr(replyto)

    sender_name = str(Header(sender_name, charset))
    replyto_name = str(Header(replyto_name, charset))

    # Create the message ('plain' stands for Content-Type: text/plain)
    try:
        msgtext = MIMEText(body.encode(charset), 'plain', charset)
    except TypeError:
        print("MIMEText fail")
        return False

    msg = MIMEMultipart()

    msg['From'] = formataddr((sender_name, sender_addr))
    msg['To'] = to #formataddr((recipient_name, recipient_addr))
    msg['Reply-to'] = formataddr((replyto_name, replyto_addr))
    msg['Subject'] = Header(subject, charset)

    msg.attach(msgtext)

    for zipfile in zipfiles:
        part = MIMEBase('application', "zip")
        b = open(zipfile, "rb").read()
        # Convert from bytes to a base64-encoded ascii string
        bs = encodebytes(b).decode()
        # Add the ascii-string to the payload
        part.set_payload(bs)
        # Tell the e-mail client that we're using base 64
        part.add_header('Content-Transfer-Encoding', 'base64')
        part.add_header('Content-Disposition', 'attachment; filename="%s"' %
                        os.path.basename(zipfile))
        msg.attach(part)

    s = SMTP()
    try:
        s.connect(server)
    except:
        print("Could not connect to smtp server: " + server)
        return False

    if username:
        s.login(username, password)
    print("Sending the e-mail")
    s.sendmail(sender, recipients, msg.as_string())
    s.quit()
    return True

def main():
    send_email()

if __name__ == "__main__":
    main()

【讨论】:

【参考方案6】:
# -*- coding: utf-8 -*-

"""
Mail sender
"""

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import smtplib
import pystache
import codecs
import time

import sys
reload(sys)
sys.setdefaultencoding('utf-8')


HOST = 'smtp.exmail.qq.com'
PORT = 587
USER = 'your@mail.com'
PASS = 'yourpass'
FROM = 'your@mail.com'

SUBJECT = 'subject'
HTML_NAME = 'tpl.html'
CSV_NAME = 'list.txt'
FAILED_LIST = []


def send(mail_receiver, mail_to):
    # text = mail_text
    html = render(mail_receiver)

    # msg = MIMEMultipart('alternative')
    msg = MIMEMultipart('mixed')
    msg['From'] = FROM
    msg['To'] = mail_to.encode()
    msg['Subject'] = SUBJECT.encode()

    # msg.attach(MIMEText(text, 'plain', 'utf-8'))
    msg.attach(MIMEText(html, 'html', 'utf-8'))

    try:
        _sender = smtplib.SMTP(
            HOST,
            PORT
        )
        _sender.starttls()
        _sender.login(USER, PASS)
        _sender.sendmail(FROM, mail_to, msg.as_string())
        _sender.quit()
        print "Success"
    except smtplib.SMTPException, e:
        print e
        FAILED_LIST.append(mail_receiver + ',' + mail_to)


def render(name):
    _tpl = codecs.open(
        './html/' + HTML_NAME,
        'r',
        'utf-8'
    )
    _html_string = _tpl.read()
    return pystache.render(_html_string, 
        'receiver': name
    )


def main():
    ls = open('./csv/' + CSV_NAME, 'r')
    mail_list = ls.read().split('\r')

    for _receiver in mail_list:
        _tmp = _receiver.split(',')
        print 'Mail: ' + _tmp[0] + ',' + _tmp[1]
        time.sleep(20)
        send(_tmp[0], _tmp[1])

    print FAILED_LIST


main()

【讨论】:

以上是关于如何使用 SMTP 发送附件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Outlook 2010(无 smtp)和 python 发送带有嵌入图像(不是附件)的 HTML 格式电子邮件

使用 smtp 发送带有多个附件的电子邮件

如何使用 Qt 发送带有图像附件的电子邮件?

使用 SMTP 作为附件发送 PDF 时出错

java程序自动发送邮件,使用的SMTP协议,如何能将发件人改成中文昵称而不是注册的那些字母。部分程序如下

如何使用 IMAP 发送邮件?