How Can I Create a Draft Email with an Attachment from Google Drive using GMail and Google Drive APIs?

When working with the Google APIs, it’s common to find yourself needing to bridge the functionalities of different services. In this blog post, I’ll walk you through how to create a draft email with an attachment stored in Google Drive using the GMail and Google Drive APIs in Node.js. We’ll start by discussing the pieces of code you already have and then move on to incorporating an attachment from Google Drive without the need to download it manually.

Current Setup for Creating Draft Emails without Attachments

You already have a functional setup for creating draft emails. Here’s the snippet you’re using to create a draft email without an attachment:

const res = await gmail.users.drafts.create({
    userId: user.email,
    requestBody: {
        message: {
            raw: await createRawMessage(to, subject, htmlBody),
        },
    },
});

The createRawMessage function formats the necessary email headers and body into a raw format that Gmail can understand:

export async function createRawMessage(recipientEmail, subject, body) {
    const message = [
        `Content-Type: text/html; charset="UTF-8"`,
        `to: ${recipientEmail}`,
        `subject: ${subject}\n`,
        body,
    ].join("\n");
    return Buffer.from(message)
        .toString("base64")
        .replace(/\+/g, "-")
        .replace(/\//g, "_")
        .replace(/=+$/, "");
}

Why Not Just Use the fileId from Google Drive?

Google’s APIs don’t allow for a direct reference of a fileId from Google Drive inside an email draft in Gmail. This means you’ll have to download the file from Google Drive, base64 encode it, and then include it in the draft email.

Using Google Drive to Fetch the File

To fetch the file from Google Drive, you’ll need to use the Google Drive API. Here’s how you can do it in steps:

  1. Authorize with the Google Drive API: You’ll need proper OAuth credentials for accessing both the Drive and Gmail APIs.
  1. Retrieve the File: Use the Drive API to get the file’s content.
  1. Base64 Encode the File Content: Prepare the file for email attachment.
  1. Compose the Email with Attachment: Include the encoded file in the email draft.

Step-by-Step Code Implementation

  1. Authorize and Initialize APIs (Assuming you have set up OAuth2.0 credentials):

const {google} = require('googleapis');
const drive = google.drive('v3');
const gmail = google.gmail('v1');
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Set credentials
oauth2Client.setCredentials({
  access_token: 'YOUR_ACCESS_TOKEN',
  refresh_token: 'YOUR_REFRESH_TOKEN',
});

  1. Fetch the File from Google Drive:

async function getFileContent(fileId) {
  const res = await drive.files.get({
    fileId: fileId,
    alt: 'media',
    auth: oauth2Client,
  }, { responseType: 'arraybuffer' });
  
  return Buffer.from(res.data, 'binary').toString('base64');
}

  1. Compose the createRawMessage Function to Include Attachment:

export async function createRawMessage(recipientEmail, subject, body, fileContent, fileName, mimeType) {
  const boundary = '----=_Part_0_1234567890.1234567890';
  
  const message = [
    `Content-Type: multipart/mixed; boundary="${boundary}"`,
    `to: ${recipientEmail}`,
    `subject: ${subject}\n\n`,
    `--${boundary}`,
    `Content-Type: text/html; charset="UTF-8"\n\n`,
    body,
    `--${boundary}`,
    `Content-Type: ${mimeType}; name="${fileName}"`,
    'Content-Transfer-Encoding: base64',
    `Content-Disposition: attachment; filename="${fileName}"\n`,
    fileContent,
    `--${boundary}--`,
  ].join("\n");
  
  return Buffer.from(message)
    .toString("base64")
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

  1. Create the Draft Email with Attachment:

const fileId = 'YOUR_FILE_ID_FROM_GOOGLE_DRIVE';
const fileContent = await getFileContent(fileId);
const fileMetadata = await drive.files.get({ fileId: fileId, fields: '*' });

const rawMessage = await createRawMessage(
  to,
  subject,
  htmlBody,
  fileContent,
  fileMetadata.data.name,
  fileMetadata.data.mimeType
);

const res = await gmail.users.drafts.create({
  userId: user.email,
  requestBody: {
    message: {
      raw: rawMessage,
    },
  },
});

Explanation and Breakdown

  1. Authorization: You set up the authorization using OAuth2. This is crucial for securely interacting with Google APIs.
  1. Fetching the File: The getFileContent function retrieves the file content using the Drive API. Note that we use responseType: 'arraybuffer' to handle binary data.
  1. Creating the Raw Message: The createRawMessage function now constructs a MIME multipart email. This ensures that the email can handle both HTML content and file attachments.
  1. Creating the Draft: Finally, we create the draft in Gmail using the gmail.users.drafts.create method, passing the formatted raw email.

By following these steps, you avoid having to download the file manually and keep the process streamlined within your Node.js application. This approach leverages both Google Drive and GMail APIs efficiently to achieve the desired functionality.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *