Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用Python-Gmail API将大文件附加到电子邮件_Python_Gmail Api_Email Attachments_Mime_Google Api Python Client - Fatal编程技术网

如何使用Python-Gmail API将大文件附加到电子邮件

如何使用Python-Gmail API将大文件附加到电子邮件,python,gmail-api,email-attachments,mime,google-api-python-client,Python,Gmail Api,Email Attachments,Mime,Google Api Python Client,我正在尝试发送一封带有附件(理想情况下是多个附件)的电子邮件,该附件的大小大于10 MB,小于总大小为25 MB的限制。我之所以提到10MB,是因为它似乎是附加文件的正常方式停止工作时的下限,并且您会得到Error 10053 我在文档中读到,实现这一点的最佳方法是使用该方法,但我无法让它工作,也无法在Python中找到任何好的示例。关于这一点的大多数问题只是链接回没有Python示例的文档,或者它们的代码导致了其他错误 我正在寻找Python的解释,因为我想确保正确理解它 我已经看过的问题:

我正在尝试发送一封带有附件(理想情况下是多个附件)的电子邮件,该附件的大小大于10 MB,小于总大小为25 MB的限制。我之所以提到10MB,是因为它似乎是附加文件的正常方式停止工作时的下限,并且您会得到
Error 10053

我在文档中读到,实现这一点的最佳方法是使用该方法,但我无法让它工作,也无法在Python中找到任何好的示例。关于这一点的大多数问题只是链接回没有Python示例的文档,或者它们的代码导致了其他错误

我正在寻找Python的解释,因为我想确保正确理解它

我已经看过的问题:

代码:

import base64
import json
import os
from email import utils, encoders
from email.message import EmailMessage
from email.mime import application, multipart, text, base, image, audio
import mimetypes

from apiclient import errors
from googleapiclient import discovery, http
from google.oauth2 import service_account

def send_email(email_subject, email_body, email_sender='my_service_account@gmail.com', email_to='', email_cc='', email_bcc='', files=None):

    # Getting credentials
    with open(os.environ.get('SERVICE_KEY_PASSWORD')) as f:
        service_account_info = json.loads(f.read())

    # Define which scopes we're trying to access
    SCOPES = ['https://www.googleapis.com/auth/gmail.send']

    # Setting up credentials using the gmail api
    credentials = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES)

    # This allows us to assign an alias account to the message so that the messages aren't coming from 'ServiceDriod-8328balh blah blah'
    delegated_credentials = credentials.with_subject(email_sender)

    # 'Building' the service instance using the credentials we've passed
    service = discovery.build(serviceName='gmail', version='v1', credentials=delegated_credentials)

    # Building out the email 
    message = multipart.MIMEMultipart()
    message['to'] = email_to
    message['from'] = email_sender
    message['date'] = utils.formatdate(localtime=True)
    message['subject'] = email_subject
    message['cc'] = email_cc
    message['bcc'] = email_bcc
    message.attach(text.MIMEText(email_body, 'html'))


    for f in files or []:
        mimetype, encoding = mimetypes.guess_type(f)

        # If the extension is not recognized it will return: (None, None)
        # If it's an .mp3, it will return: (audio/mp3, None) (None is for the encoding)
        # For an unrecognized extension we set mimetype to 'application/octet-stream' so it won't return None again. 
        if mimetype is None or encoding is not None:
            mimetype = 'application/octet-stream'
        main_type, sub_type = mimetype.split('/', 1)

        # Creating the attachement:
        # This part is used to tell how the file should be read and stored (r, or rb, etc.)
        if main_type == 'text':
            print('text')
            with open(f, 'rb') as outfile:
                attachement = text.MIMEText(outfile.read(), _subtype=sub_type)
        elif main_type == 'image':
            print('image')
            with open(f, 'rb') as outfile:
                attachement = image.MIMEImage(outfile.read(), _subtype=sub_type)
        elif main_type == 'audio':
            print('audio')
            with open(f, 'rb') as outfile:
                attachement = audio.MIMEAudio(outfile.read(), _subtype=sub_type)          
        elif main_type == 'application' and sub_type == 'pdf':   
            with open(f, 'rb') as outfile:
                attachement = application.MIMEApplication(outfile.read(), _subtype=sub_type)
        else:                              
            attachement = base.MIMEBase(main_type, sub_type)
            with open(f, 'rb') as outfile:
                attachement.set_payload(outfile.read())

        encoders.encode_base64(attachement)
        attachement.add_header('Content-Disposition', 'attachment', filename=os.path.basename(f))
        message.attach(attachement)



    media_body = http.MediaFileUpload(files[0], chunksize=500, resumable=True)
    print('Uploading large file...')
    body = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}


    message = (service.users().messages().send(userId='me', body=body, media_body=media_body).execute())
Exception has occurred: ResumableUploadError
<HttpError 400 "Bad Request">
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\http.py", line 927, in next_chunk
    raise ResumableUploadError(resp, content)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\http.py", line 822, in execute
    _, body = self.next_chunk(http=http, num_retries=num_retries)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "C:\Users\CON01599\Documents\GitHub\pipelines\components\email\send_email.py", line 105, in send_email
    message = (service.users().messages().send(userId='me', body=body, media_body=media_body).execute())
import base64
import io
import json
import os
from email import utils, encoders
from email.message import EmailMessage
from email.mime import application, multipart, text, base, image, audio
import mimetypes

from apiclient import errors
from googleapiclient import discovery, http
from google.oauth2 import service_account


def get_environment_variables():
    """ Retrieves the environment variables and returns them in
        a dictionary object.
    """
    env_var_dict = {
        'to': os.environ.get('TO'),
        'subject': os.environ.get('SUBJECT'),
        'body': os.environ.get('BODY'),
        'file': os.environ.get('FILE')
    }

    return env_var_dict


def send_email(email_subject, email_body, email_sender='my_service_account@gmail.com', email_to='', email_cc='', email_bcc='', files=None):

    # Pulling in the string value of the service key from the parameter
    with open(os.environ.get('SERVICE_KEY_PASSWORD')) as f:
        service_account_info = json.loads(f.read())

    # Define which scopes we're trying to access
    SCOPES = ['https://www.googleapis.com/auth/gmail.send']

    # Setting up credentials using the gmail api
    credentials = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES)
    # This allows us to assign an alias account to the message so that the messages aren't coming from 'ServiceDriod-8328balh blah blah'
    delegated_credentials = credentials.with_subject(email_sender)
    # 'Building' the service instance using the credentials we've passed
    service = discovery.build(serviceName='gmail', version='v1', credentials=delegated_credentials)

    # Building out the email 
    message = multipart.MIMEMultipart()
    message['to'] = email_to
    message['from'] = email_sender
    message['date'] = utils.formatdate(localtime=True)
    message['subject'] = email_subject
    message['cc'] = email_cc
    message['bcc'] = email_bcc
    message.attach(text.MIMEText(email_body, 'html'))


    for f in files or []:
        f = f.strip(' ')
        mimetype, encoding = mimetypes.guess_type(f)

        # If the extension is not recognized it will return: (None, None)
        # If it's an .mp3, it will return: (audio/mp3, None) (None is for the encoding)
        # For an unrecognized extension we set mimetype to 'application/octet-stream' so it won't return None again. 
        if mimetype is None or encoding is not None:
            mimetype = 'application/octet-stream'
        main_type, sub_type = mimetype.split('/', 1)

        # Creating the attachement:
        # This part is used to tell how the file should be read and stored (r, or rb, etc.)
        if main_type == 'text':
            print('text')
            with open(f, 'rb') as outfile:
                attachement = text.MIMEText(outfile.read(), _subtype=sub_type)
        elif main_type == 'image':
            print('image')
            with open(f, 'rb') as outfile:
                attachement = image.MIMEImage(outfile.read(), _subtype=sub_type)
        elif main_type == 'audio':
            print('audio')
            with open(f, 'rb') as outfile:
                attachement = audio.MIMEAudio(outfile.read(), _subtype=sub_type)          
        elif main_type == 'application' and sub_type == 'pdf':   
            with open(f, 'rb') as outfile:
                attachement = application.MIMEApplication(outfile.read(), _subtype=sub_type)
        else:                              
            attachement = base.MIMEBase(main_type, sub_type)
            with open(f, 'rb') as outfile:
                attachement.set_payload(outfile.read())

        encoders.encode_base64(attachement)
        attachement.add_header('Content-Disposition', 'attachment', filename=os.path.basename(f))
        message.attach(attachement)

    media_body = http.MediaIoBaseUpload(io.BytesIO(message.as_bytes()), mimetype='message/rfc822', resumable=True)
    body_metadata = {} # no thread, no labels in this example

    try:
        print('Uploading file...')
        response = service.users().messages().send(userId='me', body=body_metadata, media_body=media_body).execute()
        print(response)
    except errors.HttpError as error:
        print('An error occurred when sending the email:\n{}'.format(error))


if __name__ == '__main__':

    env_var_dict = get_environment_variables()
    print("Sending email...")
    send_email(email_subject=env_var_dict['subject'], 
            email_body=env_var_dict['body'], 
            email_to=env_var_dict['to'],
            files=env_var_dict['file'].split(','))

    print("Email sent!")
注意:现在,在MediaFileUpload中,我使用的是
文件[0]
,因为我只使用一个文件进行测试,我现在只想附加一个文件,直到它工作为止

错误:

import base64
import json
import os
from email import utils, encoders
from email.message import EmailMessage
from email.mime import application, multipart, text, base, image, audio
import mimetypes

from apiclient import errors
from googleapiclient import discovery, http
from google.oauth2 import service_account

def send_email(email_subject, email_body, email_sender='my_service_account@gmail.com', email_to='', email_cc='', email_bcc='', files=None):

    # Getting credentials
    with open(os.environ.get('SERVICE_KEY_PASSWORD')) as f:
        service_account_info = json.loads(f.read())

    # Define which scopes we're trying to access
    SCOPES = ['https://www.googleapis.com/auth/gmail.send']

    # Setting up credentials using the gmail api
    credentials = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES)

    # This allows us to assign an alias account to the message so that the messages aren't coming from 'ServiceDriod-8328balh blah blah'
    delegated_credentials = credentials.with_subject(email_sender)

    # 'Building' the service instance using the credentials we've passed
    service = discovery.build(serviceName='gmail', version='v1', credentials=delegated_credentials)

    # Building out the email 
    message = multipart.MIMEMultipart()
    message['to'] = email_to
    message['from'] = email_sender
    message['date'] = utils.formatdate(localtime=True)
    message['subject'] = email_subject
    message['cc'] = email_cc
    message['bcc'] = email_bcc
    message.attach(text.MIMEText(email_body, 'html'))


    for f in files or []:
        mimetype, encoding = mimetypes.guess_type(f)

        # If the extension is not recognized it will return: (None, None)
        # If it's an .mp3, it will return: (audio/mp3, None) (None is for the encoding)
        # For an unrecognized extension we set mimetype to 'application/octet-stream' so it won't return None again. 
        if mimetype is None or encoding is not None:
            mimetype = 'application/octet-stream'
        main_type, sub_type = mimetype.split('/', 1)

        # Creating the attachement:
        # This part is used to tell how the file should be read and stored (r, or rb, etc.)
        if main_type == 'text':
            print('text')
            with open(f, 'rb') as outfile:
                attachement = text.MIMEText(outfile.read(), _subtype=sub_type)
        elif main_type == 'image':
            print('image')
            with open(f, 'rb') as outfile:
                attachement = image.MIMEImage(outfile.read(), _subtype=sub_type)
        elif main_type == 'audio':
            print('audio')
            with open(f, 'rb') as outfile:
                attachement = audio.MIMEAudio(outfile.read(), _subtype=sub_type)          
        elif main_type == 'application' and sub_type == 'pdf':   
            with open(f, 'rb') as outfile:
                attachement = application.MIMEApplication(outfile.read(), _subtype=sub_type)
        else:                              
            attachement = base.MIMEBase(main_type, sub_type)
            with open(f, 'rb') as outfile:
                attachement.set_payload(outfile.read())

        encoders.encode_base64(attachement)
        attachement.add_header('Content-Disposition', 'attachment', filename=os.path.basename(f))
        message.attach(attachement)



    media_body = http.MediaFileUpload(files[0], chunksize=500, resumable=True)
    print('Uploading large file...')
    body = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}


    message = (service.users().messages().send(userId='me', body=body, media_body=media_body).execute())
Exception has occurred: ResumableUploadError
<HttpError 400 "Bad Request">
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\http.py", line 927, in next_chunk
    raise ResumableUploadError(resp, content)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\http.py", line 822, in execute
    _, body = self.next_chunk(http=http, num_retries=num_retries)
  File "C:\Users\CON01599\AppData\Local\Continuum\anaconda3\Lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "C:\Users\CON01599\Documents\GitHub\pipelines\components\email\send_email.py", line 105, in send_email
    message = (service.users().messages().send(userId='me', body=body, media_body=media_body).execute())
import base64
import io
import json
import os
from email import utils, encoders
from email.message import EmailMessage
from email.mime import application, multipart, text, base, image, audio
import mimetypes

from apiclient import errors
from googleapiclient import discovery, http
from google.oauth2 import service_account


def get_environment_variables():
    """ Retrieves the environment variables and returns them in
        a dictionary object.
    """
    env_var_dict = {
        'to': os.environ.get('TO'),
        'subject': os.environ.get('SUBJECT'),
        'body': os.environ.get('BODY'),
        'file': os.environ.get('FILE')
    }

    return env_var_dict


def send_email(email_subject, email_body, email_sender='my_service_account@gmail.com', email_to='', email_cc='', email_bcc='', files=None):

    # Pulling in the string value of the service key from the parameter
    with open(os.environ.get('SERVICE_KEY_PASSWORD')) as f:
        service_account_info = json.loads(f.read())

    # Define which scopes we're trying to access
    SCOPES = ['https://www.googleapis.com/auth/gmail.send']

    # Setting up credentials using the gmail api
    credentials = service_account.Credentials.from_service_account_info(service_account_info, scopes=SCOPES)
    # This allows us to assign an alias account to the message so that the messages aren't coming from 'ServiceDriod-8328balh blah blah'
    delegated_credentials = credentials.with_subject(email_sender)
    # 'Building' the service instance using the credentials we've passed
    service = discovery.build(serviceName='gmail', version='v1', credentials=delegated_credentials)

    # Building out the email 
    message = multipart.MIMEMultipart()
    message['to'] = email_to
    message['from'] = email_sender
    message['date'] = utils.formatdate(localtime=True)
    message['subject'] = email_subject
    message['cc'] = email_cc
    message['bcc'] = email_bcc
    message.attach(text.MIMEText(email_body, 'html'))


    for f in files or []:
        f = f.strip(' ')
        mimetype, encoding = mimetypes.guess_type(f)

        # If the extension is not recognized it will return: (None, None)
        # If it's an .mp3, it will return: (audio/mp3, None) (None is for the encoding)
        # For an unrecognized extension we set mimetype to 'application/octet-stream' so it won't return None again. 
        if mimetype is None or encoding is not None:
            mimetype = 'application/octet-stream'
        main_type, sub_type = mimetype.split('/', 1)

        # Creating the attachement:
        # This part is used to tell how the file should be read and stored (r, or rb, etc.)
        if main_type == 'text':
            print('text')
            with open(f, 'rb') as outfile:
                attachement = text.MIMEText(outfile.read(), _subtype=sub_type)
        elif main_type == 'image':
            print('image')
            with open(f, 'rb') as outfile:
                attachement = image.MIMEImage(outfile.read(), _subtype=sub_type)
        elif main_type == 'audio':
            print('audio')
            with open(f, 'rb') as outfile:
                attachement = audio.MIMEAudio(outfile.read(), _subtype=sub_type)          
        elif main_type == 'application' and sub_type == 'pdf':   
            with open(f, 'rb') as outfile:
                attachement = application.MIMEApplication(outfile.read(), _subtype=sub_type)
        else:                              
            attachement = base.MIMEBase(main_type, sub_type)
            with open(f, 'rb') as outfile:
                attachement.set_payload(outfile.read())

        encoders.encode_base64(attachement)
        attachement.add_header('Content-Disposition', 'attachment', filename=os.path.basename(f))
        message.attach(attachement)

    media_body = http.MediaIoBaseUpload(io.BytesIO(message.as_bytes()), mimetype='message/rfc822', resumable=True)
    body_metadata = {} # no thread, no labels in this example

    try:
        print('Uploading file...')
        response = service.users().messages().send(userId='me', body=body_metadata, media_body=media_body).execute()
        print(response)
    except errors.HttpError as error:
        print('An error occurred when sending the email:\n{}'.format(error))


if __name__ == '__main__':

    env_var_dict = get_environment_variables()
    print("Sending email...")
    send_email(email_subject=env_var_dict['subject'], 
            email_body=env_var_dict['body'], 
            email_to=env_var_dict['to'],
            files=env_var_dict['file'].split(','))

    print("Email sent!")

你提到的附件大于10Mb,但你没有提到它小于25Mb:gmail有一个限制,附件不能大于25Mb,所以如果这是你的情况,根本没有办法做到这一点,因为它超出了gmail的限制

可以找到解释


你能确认你的附件不是太大吗?

你这里的问题是你的
MediaUpload
是单个附件

您需要将整个RFC822消息作为可恢复的
MediaUpload
上传,而不是将单个附件作为可恢复的
MediaUpload
上传

换言之:

导入。。。
...
从io导入字节io
从GoogleAppClient.http导入MediaIoBaseUpload
范围=['范围']
creds=以某种方式获取凭据()
gmail=获得授权服务
msg=创建\u rfc822\消息(标题、电子邮件\u正文)
to_attach=从_dir(“../reports/tps/memos/2019/04”)获取_attachment_路径
添加附件(msg,至附件)
media=mediaobaseupload(BytesIO(msg.as_bytes()),mimetype='message/rfc822',resumable=True)
body_metadata={}#本例中没有线程,没有标签
resp=gmail.users().messages().send(userId='me',body=body\u元数据,media\u body=media).execute()
打印(resp)
#{“id”:“一些新的id”,“threadId”:“一些新的线程id”,“labelid”:[“SENT”]}
我是从你提供的代码中拼凑出来的,特别是从谷歌的收件箱到Gmail邮件导入器

在发送对现有邮件的回复时,几乎可以肯定,您应该提供某种元数据来帮助Gmail跟踪您的新回复和原始对话。也就是说,您将传递信息元数据,例如

body_元数据={'labelIds':[
“此处为您的标签id”,
“另一个标签id”],
'threadId':“您从正在回复的邮件中获取的某些线程id”
}
其他优秀参考文献:


我必须问:为什么文件必须通过电子邮件发送?也许另一种方法是将文件存储在服务器上,并授予收件人访问文件存储在该服务器上的位置的权限?共享大文件的方法有很多(FTP、云服务,如Dropbox、通过浏览器从web服务器下载等),我想说电子邮件不是一种好方法,所以我只想与您核实一下,确保电子邮件确实是实现这一点的唯一方法。@LetEpsilonBeLessThanZero这将主要用于发送报告。我不认为报告的大小会超过35MB,但不管怎样,我的印象是,当你试图通过GUI附加一个大文件时,大文件会被转换为google驱动器链接。是的,附件会低于25MB