Search code examples
python-2.7google-drive-apigmail

Convert Google Doc to PDF and send email with PDF attachment


I am attempting to convert a Google Doc to a PDF (using the Drive API) and then attaching the file to an email (using the Gmail API).

The script runs, coverts the Google Doc to a PDF, sends an email with the attachment, but the PDF attachment is blank / corrupt.

I suspect the issue is with line: msg.set_payload(fh.read())

The relevant documentation: set_payload and io.Bytes()

Any guidance is greatly appreciated.

import base64
import io
from apiclient.http import MediaIoBaseDownload
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase

fileId = '1234'
content_type = 'application/pdf'

response = drive.files().export_media(fileId=fileId, mimeType=content_type)

fh = io.BytesIO()
downloader = MediaIoBaseDownload(fh, response)
done = False
while done is False:
    status, done = downloader.next_chunk()
    logging.info("Download %d%%." % int(status.progress() * 100))

message = MIMEMultipart()
message['to'] = '[email protected]'
message['from'] = '[email protected]'
message['subject'] = 'test subject'

msg = MIMEText('test body')
message.attach(msg)

main_type, sub_type = content_type.split('/', 1)
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fh.read()) # i suspect the issue is here

msg.add_header('Content-Disposition', 'attachment', filename='an example file name.pdf')
message.attach(msg)

message_obj = {'raw': base64.urlsafe_b64encode(message.as_string())}

service.users().messages().send(userId="me", body=message_obj).execute()

Solution

  • How about this modification? I think that about the download from Google Drive, your script is correct. So I would like to propose to modify the script for sending an email with the attachment file.

    I thought that msg.set_payload(fh.read()) is one of the modification points as you say. So the data retrieved by getvalue() was converted by email.encoders.encode_base64(). And also I modified message_obj.

    Modified script:

    Please modify as follows.

    From:

    msg = MIMEText('test body')
    message.attach(msg)
    
    main_type, sub_type = content_type.split('/', 1)
    msg = MIMEBase(main_type, sub_type)
    msg.set_payload(fh.read()) # i suspect the issue is here
    
    msg.add_header('Content-Disposition', 'attachment', filename='an example file name.pdf')
    message.attach(msg)
    
    message_obj = {'raw': base64.urlsafe_b64encode(message.as_string())}
    
    service.users().messages().send(userId="me", body=message_obj).execute()
    

    To:

    from email import encoders  # Please add this.
    
    msg = MIMEText('test body')
    message.attach(msg)
    
    main_type, sub_type = content_type.split('/', 1)
    msg = MIMEBase(main_type, sub_type)
    msg.set_payload(fh.getvalue())  # Modified
    encoders.encode_base64(msg)  # Added
    
    msg.add_header('Content-Disposition', 'attachment', filename='an example file name.pdf')
    message.attach(msg)
    
    message_obj = {'raw': base64.urlsafe_b64encode(message.as_bytes()).decode()}  # Modified
    
    service.users().messages().send(userId="me", body=message_obj).execute()
    

    Note:

    • This modification supposes that you have already been able to send emails using Gmail API. If you cannot use Gmail API, please confirm the scopes and whether Gmail API is enabled at API console.

    In my environment, I could confirm that the modified script worked. But if this didn't work in your environment, I apologize.