本文描述了如何使用micropython发送一封带附件的邮件。主要使用socket链接,并使用smtp协议,构造一封带文件附件的邮件,并发送到指定邮箱。
附件是采用二进制方式读取,并一次性读入内存进行base64编码,由于我使用了esp32,内存有限,因此也不能发送太大的附件,不加spiram的情况下,几十k的附件勉强过得去。注意文件是放在esp32的根目录下的,如果不是,请自行修改路径代码。
本文的目的主要是了解如何用MIME格式去构造带附件的邮件。
作者水平有限,欢迎批评指正。
def sendMail(subjectStr,contentStr,attachFileName):
#MIME邮件结构:
#1.邮件头mailHead,详细内容见后。邮件头中会定义该邮件为multipart/mixed,并定义边界符boundary "---part1"
#2.邮件头mailHead与邮件体mailBody的分隔符(一个空行\r\n)
#3.邮件体mailBody。边界符的用法:
#----part1(第一层级的边界符,level1.part1开始)
#Content-Type: multipart/alternative;(定义第二个层级)
#boundary="----Part2"(第二个层级的边界符)
#----Part2(第二层级的边界符,level2.part1)
#Content-Type: text/plain;(第二层级第1部分的定义)
#charset="us-ascii"
#Content-Transfer-Encoding: base64
#BalaBala................内容
#----Part2(第二层级的边界符,level2.part2)
#Content-Type: text/html;(第二层级第2部分的定义)
#charset="us-ascii"
#Content-Transfer-Encoding: quoted-printable
#BalaBala................内容
#----Part2--(第二层级的结束边界符,最后多加两个--表示结束)
#----part1(第一层级的边界符,level1.part2)
#Content-Type: application/octet-stream;
# name="a.jpg"
#Content-Transfer-Encoding: base64
#Content-Disposition: attachment;
# filename="a.jpg"
#BalaBala.....内容
#----part1--(第一层结束)
#4.邮件结束。"\r\n.\r\n"
senderMailBoxAddr="fat0xff@aliyun.com"
receiverMailBoxAddr="fat0xff@aliyun.com"
mailServerAddr="smtp.aliyun.com"
mailServerPort=25
connectAddr=socket.getaddrinfo(mailServerAddr, mailServerPort)[0][-1]
mailServerCmd_MAILFROM = "MAIL FROM:<"+senderMailBoxAddr+">\r\n"
mailServerCmd_RCPTTO="RCPT TO:<"+receiverMailBoxAddr+">\r\n"
mailServerCmd_DATA = "DATA\r\n"
mailServerCmd_QUIT="QUIT\r\n"
mailHead_From="From:<"+senderMailBoxAddr+">\r\n"
mailHead_To="To:<"+receiverMailBoxAddr+">\r\n"
mailHead_Subject="Subject:"+subjectStr+"\r\n"
mailHead_XMailer="X-Mailer:shadowstar's mailer"+"\r\n"
mailHead_MIMEVer="MIME-Version: 1.0"+"\r\n"
mailHead_ContentType="Content-Type: multipart/mixed; \n"+" boundary=\"=====001_NextPart167448323667_=====\"\r\n"
#汇总mailHead
mailHead=mailHead_From+mailHead_To+mailHead_Subject+mailHead_XMailer+mailHead_MIMEVer+mailHead_ContentType
mailBody_Lv1Start="--=====001_NextPart167448323667_=====\r\n"#第一层段001开始的标识
mailBody_Lv2Dim="Content-Type: multipart/alternative;\n"+" boundary=\"=====002_NextPart167448323667_=====\"\r\n"#定义第二层段002,文本和超文本
mailBody_Lv2Start="--=====002_NextPart167448323667_=====\r\n"#第二层段开始
mailBody_Lv2Part1ContentType="Content-Type: text/plain; charset=\"UTF-8\"\r\n"
mailBody_Lv2Part1ContentCode="Content-Transfer-Encoding: base64\r\n"
mailBody_Lv2Part1Content=ubinascii.b2a_base64(contentStr.encode()).decode()+"\r\n"
mailBody_Lv2End="--=====002_NextPart167448323667_=====--\r\n"#第二层段结束
#汇总mailBodyLv2
mailBody_Lv2=mailBody_Lv2Dim+mailBody_Lv2Start+mailBody_Lv2Part1ContentType+mailBody_Lv2Part1ContentCode\
+mailBody_Lv2Part1Content+mailBody_Lv2End
mailBodyLv1_ContentCode="Content-Transfer-Encoding: base64\r\n"
mailBodyLv1_ContentType="Content-Type:image/jpeg; name=\"a.jpg\"\r\n"
mailBodyLv1_ContentDispo="Content-Disposition: attachment; filename=\"a.jpg\"\r\n\r\n"
file=open(attachFileName,"rb")
filebuf=file.read()
time.sleep(3)
mailBodyLv1_Content=ubinascii.b2a_base64(filebuf).decode()+"\r\n"
mailBodyLv1_End="\n--=====001_NextPart167448323667_=====--\r\n\r\n"
#LV1过大,不再汇总
mailBody=mailBody_Lv1Start+mailBody_Lv2+mailBody_Lv1Start+mailBodyLv1_ContentCode+mailBodyLv1_ContentType\
+mailBodyLv1_ContentDispo+mailBodyLv1_Content+mailBodyLv1_End
#邮件头与正文间用一个空行作为隔开标志。邮件必须以"\r\n.\r\n"结尾
msg=mailHead+"\r\n"+mailBody+"\r\n.\r\n"
print(len(msg))
clientSocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientSocket.connect(connectAddr)
recv=clientSocket.recv(1024)
print(recv)
heloCommand="HELO Alice\r\n"
clientSocket.send(bytes(heloCommand.encode()))
recv1=clientSocket.recv(1024)
print(recv1.decode())
loginini = b'AUTH LOGIN\r\n'
userCommand=ubinascii.b2a_base64(b'fat0xff@aliyun.com').decode()+'\r\n'
passwordCommand=ubinascii.b2a_base64(b'YourMailBoxPassword').decode()+'\r\n'
clientSocket.send(loginini)
recv2 = clientSocket.recv(1024).decode('utf-8')
print('222+ ',recv2)
clientSocket.send(userCommand.encode())
recv3 = clientSocket.recv(1024).decode('utf-8')
print('333+ ',recv3)
clientSocket.send(passwordCommand.encode())
recv4 = clientSocket.recv(1024).decode('utf-8')
print('444+ ',recv4)
print('-----------------------------------')
clientSocket.send(bytes(mailServerCmd_MAILFROM.encode())) #send mail, had to convert to bytes
check=clientSocket.recv(1024)
print(check)
#Send RCPT TO command and print server response.
clientSocket.send(bytes(mailServerCmd_RCPTTO.encode())) #recieve, had to convert to bytes
check1=clientSocket.recv(1024)
print(check1) #print confirmation of working messages
#Send DATA command and print server response.
clientSocket.send(bytes(mailServerCmd_DATA.encode())) #DATA, had to convert to bytes
check2=clientSocket.recv(1024)
print(check2) #print confirmation of working messages
#Message ends with a single period.
#clientSocket.send(bytes((mailHead_From+mailHead_To+mailHead_Subject+msg+endmsg).encode())) #Concatinate bits for message
clientSocket.send(bytes((msg).encode())) #Concatinate bits for message
check3=clientSocket.recv(1024)
print(check3) #print confirmation of working messages
#Send QUIT command and get server response.
clientSocket.send(bytes(mailServerCmd_QUIT.encode())) #Quit the session
check4=clientSocket.recv(1024)
print(check4)
wifi_connect("wifiname","wifipassword")
sendMail("This is tile!","This is mail content!","a.jpg")
print(micropython.mem_info())
gc.collect()#邮件发送附件占用内存太大,发送完了即刻清理内存!
print(micropython.mem_info())