Google-api-nodejs-client: Unable to send email with attachment with GMail API

Created on 10 Sep 2018  路  2Comments  路  Source: googleapis/google-api-nodejs-client

Environment details

I want to send emails via the Gmail. send API endpoint WITH ATTACHMENT but I am unable to do so.
Note: I am able to send emails without attachment.
The following is my method which is not working that I have advanced from the method provided by Google on
https://developers.google.com/gmail/api/quickstart/nodejs

module.exports.sendMailAttach = function (to, subject, message, pdfPath, callback) {
    const fs = require('fs');
    const readline = require('readline');
    const {google} = require('googleapis');
    const path = require('path');

  // If modifying these scopes, delete token.json.
    var SCOPES = [
        'https://mail.google.com/',
        'https://www.googleapis.com/auth/gmail.modify',
        'https://www.googleapis.com/auth/gmail.compose',
        'https://www.googleapis.com/auth/gmail.send'
    ];
    const TOKEN_PATH = '/home/................../config/token.json';

// Load client secrets from a local file.
    fs.readFile('/home/................../config/credentials.json', (err, content) => {
        if (err) return console.log('Error loading client secret file:', err);
        // Authorize a client with credentials, then call the Gmail API.
        authorize(JSON.parse(content), sendMail);
    });

    function authorize(credentials, callback) {
        const {client_secret, client_id, redirect_uris} = credentials.installed;
        const oAuth2Client = new google.auth.OAuth2(
            client_id, client_secret, redirect_uris[0]);

        // Check if we have previously stored a token.
        fs.readFile(TOKEN_PATH, (err, token) => {
            if (err) return getNewToken(oAuth2Client, callback);
            oAuth2Client.setCredentials(JSON.parse(token));
            callback(oAuth2Client);
        });
    }

    function getNewToken(oAuth2Client, callback) {
        const authUrl = oAuth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: SCOPES,
        });
        console.log('Authorize this app by visiting this url:', authUrl);
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        rl.question('Enter the code from that page here: ', (code) => {
            rl.close();
            oAuth2Client.getToken(code, (err, token) => {
                if (err) return console.error('Error retrieving access token', err);
                oAuth2Client.setCredentials(token);
                // Store the token to disk for later program executions
                fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
                    if (err) return console.error(err);
                    console.log('Token stored to', TOKEN_PATH);
                });
                callback(oAuth2Client);
            });
        });
    }

    function makeBody(to, from, subject, message, pdfPath) {

        const attach = `${Buffer.from(fs.readFileSync(pdfPath)).toString('base64')}`;
        const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
        const messageParts = [
            `From: ${from}`,
            `To: ${to}`,
            'MIME-Version: 1.0',
            `Subject: ${utf8Subject}`,
            "Content-Type: application/pdf; name="+path.basename(pdfPath),
            "Content-Transfer-Encoding: 7bit",
            "Content-Disposition: attachment; filename="+path.basename(pdfPath)
        ];
            // "Content-Transfer-Encoding: base64",
        const mailMessage = messageParts.join('\n');

        const encodedMessage = Buffer.from(mailMessage)
            .toString('base64')
            .replace(/\+/g, '-')
            .replace(/\//g, '_')
            .replace(/=+$/, '');


        return encodedMessage;
    }

    function sendMail(auth) {
        var raw = makeBody(to, 'me', subject, message, pdfPath);

        const gmail = google.gmail({version: 'v1', auth});

        gmail.users.messages.send({
            auth: auth,
            userId: 'me',
            uploadType: 'multipart',
            resource: {
                raw: raw
            }
        }, (err, res) => {
            if (err) return console.log('The API returned an error: ' + err);
            console.log(`Mail sent to ${to}   res.status ${res.status}, res.statusText ${res.statusText} res.data ${res.data}`)
            callback(err, res);
        });
    }
};
This is my function call
sendMailAttach('[email protected]','Email with Attachment',"testBody",/home/..../Downloads/ADD LOCATION API.PDF, function(error, response){
    console.log("response",response);
})
gmail question

Most helpful comment

I'm using the following structure to send a message with an attachment:

    const messageParts = [
      'From: ' + from,
      'To: ' + recipients,
      `Subject: ${subject}`,
      'MIME-Version: 1.0',
      'Content-Type: multipart/mixed; boundary="' + mixedB + '"',
      '',
      '--' + mixedB,
      'Content-Type: multipart/related; boundary="' + relatedB + '"',
      '',
      '--' + relatedB,
      'Content-Type: multipart/alternative; boundary="' + alternativeB + '"',
      '',
      '--' + alternativeB,
      'Content-Type: text/html; charset=utf-8',
      '',
      content, // html content.
      '',
      '--' + alternativeB + '--',
      '',
      '--' + relatedB + '--',
      '',
      '--' + mixedB,
      'Content-Type: application/pdf;name="' + config.attachmentFilename + '.pdf"',
      'Content-Transfer-Encoding: base64',
      'Content-Disposition: attachment;filename="' + config.attachmentFilename + '.pdf"',
      '',
      config.attachment.slice(config.attachment.indexOf(',') + 1), // base64 data of the file.
      '',
      '--' + mixedB + '--'
    ]

This web page help me a lot to learn about structuring the content of an email: https://www.qcode.co.uk/post/70

I just wanted to share this snippet because most issues when send an email with an attachment are because of wrong structuring.

All 2 comments

I'm using the following structure to send a message with an attachment:

    const messageParts = [
      'From: ' + from,
      'To: ' + recipients,
      `Subject: ${subject}`,
      'MIME-Version: 1.0',
      'Content-Type: multipart/mixed; boundary="' + mixedB + '"',
      '',
      '--' + mixedB,
      'Content-Type: multipart/related; boundary="' + relatedB + '"',
      '',
      '--' + relatedB,
      'Content-Type: multipart/alternative; boundary="' + alternativeB + '"',
      '',
      '--' + alternativeB,
      'Content-Type: text/html; charset=utf-8',
      '',
      content, // html content.
      '',
      '--' + alternativeB + '--',
      '',
      '--' + relatedB + '--',
      '',
      '--' + mixedB,
      'Content-Type: application/pdf;name="' + config.attachmentFilename + '.pdf"',
      'Content-Transfer-Encoding: base64',
      'Content-Disposition: attachment;filename="' + config.attachmentFilename + '.pdf"',
      '',
      config.attachment.slice(config.attachment.indexOf(',') + 1), // base64 data of the file.
      '',
      '--' + mixedB + '--'
    ]

This web page help me a lot to learn about structuring the content of an email: https://www.qcode.co.uk/post/70

I just wanted to share this snippet because most issues when send an email with an attachment are because of wrong structuring.

Thanks for the awesome answer!

Was this page helpful?
0 / 5 - 0 ratings