Python网络编程:E-mail服务(五)深入理解email模块的message和MIME类

简介

本文主要介绍Python标准库email的message和MIME类,并分析了相关的实现,能够是读者更好的使用email模块。

核心类Message

Message类是email的核心类, 它是email对象模型中基类,提供了设置和查询邮件头部,访问消息体的核心方法。从概念上讲,Message对象构成了邮件头部(Headers)和消息体(payloads)。头部格式在RFC 2822中进行了定义,每个头部由该项名字和值组成,并由冒号分割。消息体可以是简单消息对象的字符串或多个MIME容器的Message对象组成的多部分邮件。Message类在email.message模块中定义。
Message基类与MIME类的继承关系如下图所示:



MIMEBase基类

MIMEBase作为MIME相关对象基类继承了Message,拥有拥有Message操作邮件头部和邮件体的所有函数。MIME在邮件头部增加了Content-Type和MIME-Version两个头部信息,从下面MIMEBase的实现中可以清楚的看到这一点:
class MIMEBase(message.Message):
    """Base class for MIME specializations."""

    def __init__(self, _maintype, _subtype, **_params):
        """This constructor adds a Content-Type: and a MIME-Version: header.

        The Content-Type: header is taken from the _maintype and _subtype
        arguments.  Additional parameters for this header are taken from the
        keyword arguments.
        """
        message.Message.__init__(self)
        ctype = '%s/%s' % (_maintype, _subtype)
        self.add_header('Content-Type', ctype, **_params)
        self['MIME-Version'] = '1.0'

多部分邮件类MIMEMultipart类

MIMEMultipart类用于实现多部分邮件的功能,缺省情况下它会创建Content-Type类型为mulitpart/mixed邮件。
class MIMEMultipart(MIMEBase):
    """Base class for MIME multipart/* type messages."""

    def __init__(self, _subtype='mixed', boundary=None, _subparts=None,
                 **_params):
        """Creates a multipart/* type message.

        By default, creates a multipart/mixed message, with proper
        Content-Type and MIME-Version headers.

        _subtype is the subtype of the multipart content type, defaulting to
        `mixed'.

        boundary is the multipart boundary string.  By default it is
        calculated as needed.

        _subparts is a sequence of initial subparts for the payload.  It
        must be an iterable object, such as a list.  You can always
        attach new subparts to the message by using the attach() method.

        Additional parameters for the Content-Type header are taken from the
        keyword arguments (or passed into the _params argument).
        """
        MIMEBase.__init__(self, 'multipart', _subtype, **_params)

        # Initialise _payload to an empty list as the Message superclass's
        # implementation of is_multipart assumes that _payload is a list for
        # multipart messages.
        self._payload = []

        if _subparts:
            for p in _subparts:
                self.attach(p)
        if boundary:
            self.set_boundary(boundary)
在类初始化时,会将_payload初始化为空的列表,因为在Message超类中is_multipart方法假设_payload是一个列表,并用来存放多部分邮件内容,利用attach()方法可以将多部分邮件添加到列表中。这里来看一下超类Message中的相关实现:
class Message:
	......

    def is_multipart(self):
        """Return True if the message consists of multiple parts."""
        return isinstance(self._payload, list)

    #
    # Payload manipulation.
    #
    def attach(self, payload):
        """Add the given payload to the current payload.

        The current payload will always be a list of objects after this method
        is called.  If you want to set the payload to a scalar object, use
        set_payload() instead.
        """
        if self._payload is None:
            self._payload = [payload]
        else:
            self._payload.append(payload)
			
	......

非多部分邮件类MIMENonMultipart类

MIMENonMultipart类与MIMEMultipart类一样,继承自MIMEBase基类,其实现了自定义attach()方法,由于其是非多部分邮件类型实现,用户调用此方法,会抛出MutlipartConversionError异常。
class MIMENonMultipart(MIMEBase):
    """Base class for MIME non-multipart type messages."""

    def attach(self, payload):
        # The public API prohibits attaching multiple subparts to MIMEBase
        # derived subtypes since none of them are, by definition, of content
        # type multipart/*
        raise errors.MultipartConversionError(
            'Cannot attach additional subparts to non-multipart/*')
MIMENonMultipart类是其它具体MIMEApplication, MIMEText,MIMEImage等其它类的基类,也就是说它们不运行用户使用attach()方法。它们是通过set_payload()方法来实现设置邮件payload功能的。
class Message:
	......

    def set_payload(self, payload, charset=None):
        """Set the payload to the given value.

        Optional charset sets the message's default character set.  See
        set_charset() for details.
        """
        self._payload = payload
        if charset is not None:
            self.set_charset(charset)
			
	......

具体类实现

MIME模块提供了5个MIME具体类,各个具体类都提供了与名称对应的主消息类型的对象的支持,它们都继承了MIMENonMultipart类;关于MIME主类型的知识,可以参考 Python网络编程:E-mail服务(三)MIME详解。这里简单看一下MIMEText的实现:
class MIMEText(MIMENonMultipart):
    """Class for generating text/* type MIME documents."""

    def __init__(self, _text, _subtype='plain', _charset='us-ascii'):
        """Create a text/* type MIME document.

        _text is the string for this message object.

        _subtype is the MIME sub content type, defaulting to "plain".

        _charset is the character set parameter added to the Content-Type
        header.  This defaults to "us-ascii".  Note that as a side-effect, the
        Content-Transfer-Encoding header will also be set.
        """
        MIMENonMultipart.__init__(self, 'text', _subtype,
                                  **{'charset': _charset})
        self.set_payload(_text, _charset)
MIMEText在初始化时,将主类型设置为text类型,并通过set_payload()函数设置邮件体内容。

总结

结合E-mail的核心类Message和MIME相关类的实现,可以更深入的了解通过email标准库编写邮件的机制,高效的实现编写邮件相关的代码。




  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的 C++ 程序示例,用于发送 E-Mail,利用 SMTP 协议、Base64 编码和 MIME 格式。 ```c++ #include <iostream> #include <string> #include <cstdlib> #include <cstring> #include <sstream> #include <fstream> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") using namespace std; // 定义 Base64 编码的转换表 const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Base64 编码函数 string base64_encode(const unsigned char* input, int length) { string output; int i = 0, j = 0; unsigned char char_array_3[3], char_array_4[4]; while (length--) { char_array_3[i++] = *(input++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (i = 0; (i < 4); i++) output += base64_table[char_array_4[i]]; i = 0; } } if (i) { for (j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) output += base64_table[char_array_4[j]]; while ((i++ < 3)) output += '='; } return output; } // MIME 格式 string create_mime(string from, string to, string subject, string content) { string mime; mime += "From: " + from + "\r\n"; mime += "To: " + to + "\r\n"; mime += "Subject: " + subject + "\r\n"; mime += "MIME-Version: 1.0\r\n"; mime += "Content-Type: text/plain; charset=utf-8\r\n"; mime += "Content-Transfer-Encoding: base64\r\n"; mime += "\r\n"; mime += base64_encode((const unsigned char*)content.c_str(), content.length()); mime += "\r\n.\r\n"; return mime; } // 发送邮件 bool send_mail(string server, string username, string password, string from, string to, string subject, string content) { // 创建连接 SOCKET sock; struct sockaddr_in serv_addr; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(25); serv_addr.sin_addr.s_addr = inet_addr(server.c_str()); if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { cerr << "Connect error!" << endl; return false; } // 接收信息 char buffer[1024] = { 0 }; recv(sock, buffer, sizeof(buffer), 0); // 发送 HELO 命令 string helo = "HELO " + server + "\r\n"; send(sock, helo.c_str(), helo.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送 AUTH LOGIN 命令 string auth_login = "AUTH LOGIN\r\n"; send(sock, auth_login.c_str(), auth_login.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送用户名 string base64_username = base64_encode((const unsigned char*)username.c_str(), username.length()); send(sock, base64_username.c_str(), base64_username.length(), 0); send(sock, "\r\n", 2, 0); recv(sock, buffer, sizeof(buffer), 0); // 发送密码 string base64_password = base64_encode((const unsigned char*)password.c_str(), password.length()); send(sock, base64_password.c_str(), base64_password.length(), 0); send(sock, "\r\n", 2, 0); recv(sock, buffer, sizeof(buffer), 0); // 发送 MAIL FROM 命令 string mail_from = "MAIL FROM: <" + from + ">\r\n"; send(sock, mail_from.c_str(), mail_from.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送 RCPT TO 命令 string rcpt_to = "RCPT TO: <" + to + ">\r\n"; send(sock, rcpt_to.c_str(), rcpt_to.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送 DATA 命令 string data = "DATA\r\n"; send(sock, data.c_str(), data.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送邮件内容 string mime = create_mime(from, to, subject, content); send(sock, mime.c_str(), mime.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 发送 QUIT 命令 string quit = "QUIT\r\n"; send(sock, quit.c_str(), quit.length(), 0); recv(sock, buffer, sizeof(buffer), 0); // 关闭连接 closesocket(sock); return true; } int main() { // 设置发送邮件的参数 string server = "smtp.gmail.com"; string username = "your_username@gmail.com"; string password = "your_password"; string from = "your_username@gmail.com"; string to = "[email protected]"; string subject = "Test Mail"; string content = "Hello World!"; // 发送邮件 if (send_mail(server, username, password, from, to, subject, content)) { cout << "Mail sent successfully!" << endl; } else { cerr << "Failed to send mail!" << endl; } return 0; } ``` 请注意,在实际使用中,您需要根据您选择的邮件服务器和帐户来调整程序中的参数。此外,该程序仅适用于 Windows 平台。如果您需要在其他平台上运行,您需要对代码进行相应调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值