Skip to main content
 首页 » 编程设计

python 3.6 gmail api——发送带附件的电子邮件

2025年05月04日28youxin

这个 python 3 脚本假设创建一个电子邮件,将一个文件附加到它(使用它的 url)并发送它。它发送电子邮件,但 create_message_with_attachment()

出现问题

TypeError: Attach is not valid on a message with a non-multipart payload

我确实阅读了谷歌文档。讨论它的堆栈线程专注于奇特的附件样式,同时在它的顶部混合了 python 版本的不同语法。

下面的代码是几个来源的拼凑而成。我努力在 create_message_with_attachment() 中将它们连接在一起。

例如,我不知道我是否应该包括这个(它来自 create_message_without_attachment() ,它适用于这段代码。Cf 在底部)

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

带有附件代码的创建消息:

import httplib2 
import os 
import oauth2client 
from oauth2client import client, tools 
import base64 
from email import encoders 
 
#needed for attachment 
from email.mime.multipart import MIMEMultipart 
from email.mime.text import MIMEText 
 
#needed for gmail service 
from apiclient import errors, discovery   
 
#The scope URL for read/write access to the gmail api  
SCOPES = 'https://www.googleapis.com/auth/gmail.send' 
 
CLIENT_SECRET_FILE = 'client_secret.json' 
APPLICATION_NAME = 'Gmail API Python Send Email' 
 
 
def get_credentials(): 
    # If needed create folder for credential 
    home_dir = os.path.expanduser('~') #>> C:\Users\me 
    credential_dir = os.path.join(home_dir, '.credentials') # >>C:\Users\me\.credentials   (it's a folder) 
    if not os.path.exists(credential_dir): 
        os.makedirs(credential_dir)  #create folder if doesnt exist 
    credential_path = os.path.join(credential_dir, 'gmail-python-email-send.json') 
 
    #Store the credential 
    store = oauth2client.file.Storage(credential_path) 
    credentials = store.get() 
 
    if not credentials or credentials.invalid: 
    # Create a flow object. (it assists with OAuth 2.0 steps to get user authorization + credentials) 
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) 
        flow.user_agent = APPLICATION_NAME 
        credentials = tools.run_flow(flow, store) 
        print('Storing credentials to ' + credential_path) 
 
    return credentials 
 
 
 
def SendMessage(sender, to, subject, msgHtml, msgPlain): 
    credentials = get_credentials()  
 
    http = httplib2.Http()  # Create an httplib2.Http object to handle our HTTP requests, and authorize it using credentials.authorize() 
 
    # http is the authorized httplib2.Http()  
    http = credentials.authorize(http) 
 
    service = discovery.build('gmail', 'v1', http=http) 
 
    message_with_attach = create_message_without_attachment(sender, to, subject, msgHtml, msgPlain) 
    SendMessageInternal(service, "me", message_with_attach) 
 
 
def SendMessageInternal(service, user_id, message):  
    try: 
        message = (service.users().messages().send(userId=user_id, body=message).execute())  ####need  to get user_id before 
 
        message_ID = message['id'] 
        print(f'Message Id: {message_ID}') 
        return [message, message_ID] #return value as list 
    except errors.HttpError as error: 
        print(f'An error occurred: {error}')     
 
def create_message_with_attachment(sender, to, subject, msgHtml, msgPlain): 
 
    # multipart container can contain other MIME parts.  (attachment will be independent of the multipart/alternative) 
    msg = MIMEMultipart('alternative') 
    msg['To'] = to 
    msg['From'] = sender 
    msg['Subject'] = subject 
 
    # convert both part to a MIME compatible string 
    part1 = MIMEText(msgPlain, 'plain')  
    part2 = MIMEText(msgHtml, 'html') 
 
    # create .txt attachment 
    filePath=r"C:\Users\me\Desktop\test_Attachment.txt" 
    myFile=open(filePath, "rb") 
    attachment= MIMEApplication(myFile.read()) 
    msg.set_payload(myFile) #  
    myFile.close() 
    msg.set_payload(myFile) #  
    myFile.close() 
 
    #This will add a header that looks like: "Content-Disposition: attachment; filename="test_Attachment.txt" " 
    attachment.add_header('content-disposition', 'attachment', filename = ('utf-8', '', 'test_Attachment.txt')) 
 
    # Attach parts into message container. 
    msg.attach(attachment) 
    msg.attach(part1) 
    msg.attach(part2) 
 
    # Encode the payload using Base64. 
    raw = encoders.encode_base64(msg) 
    return raw 
 
 
def main(): 
    to = "youremail@gmail.com" 
    sender = "myemail@gmail.com" 
    subject = "subject test1" 
    msgHtml = r'Hi<br/>Html <b>hello</b>' 
    msgPlain = "Hi\nPlain Email" 
    message_text= "this is message text" 
    SendMessage(sender, to, subject, msgHtml, msgPlain) 
 
 
if __name__ == '__main__': 
    main() 

此函数在这段代码中成功发送不带附件的电子邮件:

def create_message_without_attachment (sender, to, subject, msgHtml, msgPlain): 
    msg = MIMEMultipart('alternative') 
    msg['Subject'] = subject 
    msg['From'] = sender 
    msg['To'] = to 
    msg.attach(MIMEText(msgPlain, 'plain')) 
    msg.attach(MIMEText(msgHtml, 'html')) 
 
    raw = base64.urlsafe_b64encode(msg.as_bytes()) 
    raw = raw.decode() 
    body = {'raw': raw} 
    return body 

请您参考如下方法:

Edit1(避免重复相同的答案):你会在 this 中找到answer,发送带(或不带)附件的电子邮件所需的代码(和解释)。

Edit2:由于 randomfigure 改进了代码