Search code examples
pythongoogle-drive-apigmail-apigoogle-docs-apigoogle-api-python-client

How to attach a Google Drive file in message and send through gmail API (without downloading the file)?


I'm currently working on a project where I need to send mails to users and attach some documents from Google Docs.

I have the file id's of the documents to be sent. I don't want to download the file and then attach it to the message. Is there any way to attach files directly from google drive without downloading them to our local storage ?

Methods I have tried -

  1. I first tried to export the file and then store the byte like object in a variable and then pass it to the create_message() method. But the mimeType.guess_type() expects an string like object which is either the path or an url.

  2. Then I tried to directly pass the drive url to the create_message() method but no success.

Here's my create_message method -

def create_message_with_attachment(self, sender, to, subject, message_text,files):
    """Create a message for an email.

    Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.
    file: The path to the file to be attached.

    Returns:
    An object containing a base64url encoded email object.
    """
    message = MIMEMultipart()
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject

    msg = MIMEText(message_text)
    message.attach(msg)

    for fil in files:
        content_type, encoding = mimetypes.guess_type(fil)

        if content_type is None or encoding is not None:
            content_type = 'application/octet-stream'
        main_type, sub_type = content_type.split('/', 1)
        if main_type == 'text':
            fp = open(fil, 'rb')
            msg = MIMEText(fp.read(), _subtype=sub_type)
            fp.close()
        elif main_type == 'image':
            fp = open(fil, 'rb')
            msg = MIMEImage(fp.read(), _subtype=sub_type)
            fp.close()
        elif main_type == 'audio':
            fp = open(fil, 'rb')
            msg = MIMEAudio(fp.read(), _subtype=sub_type)
            fp.close()
        else:
            fp = open(fil, 'rb')
            msg = MIMEBase(main_type, sub_type)
            msg.set_payload(fp.read())
            fp.close()
        filename = os.path.basename(fil)
        msg.add_header('Content-Disposition', 'attachment', filename=filename)
        message.attach(msg)
    b64_bytes = base64.urlsafe_b64encode(message.as_bytes())
    b64_string = b64_bytes.decode()
    body = {'raw': b64_string}
    return body

The files parameter is array because i want to send multiple attachments around 3-4.

So far there has been no luck. Can anyone suggest other methods to achieve this ?


Solution

  • The problem with Google Apps is that you cannot preserve the data type when downloading or exporting them - they need to be converted to a different MIMEType. Thus, If you have problems sending e-mails with an attachment, I suggest you to do the following workaround: Include in your e-mail a link to the file, rather than the file itself. In this case you have to perform the following steps:

    Share the file of interest with the respective user. This is something you can do programmatically by creating and updating permissions. In your case, you need to specify emailAddress, the fileID, as well as include in the request body the role you want to give to the email recipient. In case you update an existing permission, you also need to specify the permissionId. Include the webViewLink of the desired file in your email body. You can obtain the webViewLink by listing files and specifying the webViewLink as a field you want to obtain in your response