繁体   English   中英

如何在 Node.js 中使用 Gmail API 发送带附件的电子邮件?

[英]How to send an email with attachment using Gmail API in Node.js?

我是 Node.js 的新手,我正在尝试使用 Gmail API 创建一个邮箱,除了在电子邮件中上传附件外,一切正常。 我找到了 Java、Python 和 C# 的例子,但我找不到任何关于节点的文档。 任何提示将不胜感激。

这是我的代码:

function makeBody(to, from, subject, message) {
    var str = ["Content-Type: multipart/mixed; charset=\"UTF-8\"\n",
        "MIME-Version: 1.0\n",
        "Content-Transfer-Encoding: 7bit\n",
        "to: ", to, "\n",
        "from: ", from, "\n",
        "subject: ", subject, "\n\n",
        message,
        file
    ].join('');

    var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');

    return encodedMail;
}

function sendMessage(auth) {
    var raw = makeBody(tap, 'me', response.subject, response.content, response.files);
    gmail.users.messages.send({
        auth: auth,
        userId: 'me',
        resource: {
            raw: raw
        }
    }, function (err, response) {
        if (err) {
            console.log('Error  ' + err);
            return;
        }

        if (response) {
            res.sendFile(__dirname + '/boite.html')
        }
    });
}

这可能会有点晚,无论如何我会花时间以防万一有人以后想要一个替代方案。

Moxched 方法的主要问题是他可能需要仔细研究 MIME 规范(这对我来说是一个很大的痛苦)以更好地理解发送附件所必需的一些东西。

从我的立场来看,要能够使用 gmail API 发送附件和许多其他内容,您必须根据 MIME 规范构建所有请求,为此您需要了解 MIME 中的事物如何工作,包括边界。

Joris 方法有效但最终没有使用 nodeJS 库发送电子邮件。 他无法将gmail-api-create-message-body包中的答案与 gmail API 一起使用的原因是由于某种原因,该库在其 MIME 消息的顶部生成了以下内容:

'Content-Type: multipart/related; boundary="foo_bar_baz"',
`In-Reply-To: fakeemail@gmail.com`,
`References: `,
`From: fakeemail2@gmail.com`,
`Subject: SUBJECT`,
`MIME-Version: 1.0`,
'',
`--foo_bar_baz`,
`Content-Type: application/json; charset="UTF-8"`,
'',
`{`,
`}`,
'',
`--foo_bar_baz`,
`Content-Type: message/rfc822`,
'',
...

出于某种原因,gmailAPI 不喜欢这样...

我的建议是更好地理解 MIME 规范,一个非常简单的方法是使用一些旧的逆向工程,为此我建议查看来自gmail-api-create-message-bodymail-composer的回复节点邮件。

使用nodemailer/lib/mail-composer你将能够根据 MIME 规范轻松生成必要的 MIME 消息,它包括附件支持和所有其他东西。 生成的 MIME 消息与 Gmail API 兼容。 我留下了一个基于 NodeJS 文档示例的工作示例,该示例发送一封包含 2 个附件的电子邮件。

希望这可以帮助!

 const fs = require('fs'); const path = require('path'); const readline = require('readline'); const {google} = require('googleapis'); const MailComposer = require('nodemailer/lib/mail-composer'); // If modifying these scopes, delete token.json. const SCOPES = [ 'https://mail.google.com', 'https://www.googleapis.com/auth/gmail.readonly' ]; const TOKEN_PATH = 'token.json'; // Load client secrets from a local file. fs.readFile('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), listLabels); authorize(JSON.parse(content), sendEmail); }); /** * Create an OAuth2 client with the given credentials, and then execute the * given callback function. * @param {Object} credentials The authorization client credentials. * @param {function} callback The callback to call with the authorized client. */ 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); }); } /** * Get and store new token after prompting for user authorization, and then * execute the given callback with the authorized OAuth2 client. * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for. * @param {getEventsCallback} callback The callback for the authorized client. */ 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 sendEmail(auth) { // ----------nodemailer test---------------------------------------------------- let mail = new MailComposer( { to: "FAKE_EMAIL@gmail.com", text: "I hope this works", html: " <strong> I hope this works </strong>", subject: "Test email gmail-nodemailer-composer", textEncoding: "base64", attachments: [ { // encoded string as an attachment filename: 'text1.txt', content: 'aGVsbG8gd29ybGQh', encoding: 'base64' }, { // encoded string as an attachment filename: 'text2.txt', content: 'aGVsbG8gd29ybGQh', encoding: 'base64' }, ] }); mail.compile().build( (error, msg) => { if (error) return console.log('Error compiling email ' + error); const encodedMessage = Buffer.from(msg).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); const gmail = google.gmail({version: 'v1', auth}); gmail.users.messages.send({ userId: 'me', resource: { raw: encodedMessage, } }, (err, result) => { if (err) return console.log('NODEMAILER - The API returned an error: ' + err); console.log("NODEMAILER - Sending email reply from server:", result.data); }); }) // ----------nodemailer test---------------------------------------------------- }

被困在同一个问题上,我设法通过左右抓取东西来构建解决方案。

你需要使用的是 npm 包gmail-api-create-message-body

npm 包页面

  const body = createBody({
    headers:{
      To:(msg.to.name) + " <" + msg.to.email + ">",
      From:(msg.from.name) + " <" + msg.from.email + ">",
      Subject:msg.subject
    },
    textHtml:msg.body.html,
    textPlain:msg.body.text,
    attachments:msg.files
  })

这些files是以下格式的数组。 这只是一个例子:

    files: [{
      type: "image/jpeg",
      name: "id1.jpg",
      data:base64ImageData
    }, {
      type: "image/jpeg",
      name: "id2.jpg",
      data: base64ImageData
    }]

接下来我需要混合 2 个 api。 我想通过 Google API 做所有事情,但那没有用,我不想浪费时间去理解为什么(他们的文档+节点示例是一场灾难)

为了进行调用,我们需要身份验证令牌。 这可以使用 npm 包google-auth-library找到

await oauth2Client.getAccessToken()

我认为有关如何使用 Google 进行 OAuth2 的完整详细信息超出了此答案的范围。

接下来我们需要实际发送邮件。 我不可能让它与官方 Gmail api 一起工作(不断收到Error: Recipient address required ),所以我使用了request-promise ,如gmail-api-create-message-body的示例所示

await rp({
  method: 'POST',
  uri: 'https://www.googleapis.com/upload/gmail/v1/users/me/messages/send',
  headers: {
    Authorization: `Bearer ${oauth2Client.credentials.access_token}`,
    'Content-Type': 'multipart/related; boundary="foo_bar_baz"'
  },
  body: body
});

这一切都很完美。

Creating messages with attachments中有关于此的说明:

创建带附件的消息与创建任何其他消息类似,但将文件作为多部分 MIME 消息上传的过程取决于编程语言。

对于 NodeJS 示例参考,请查看此SO 帖子

我能够通过使用 nodemailer 邮件编辑器来做到这一点。 这是基于 nodejs 快速入门的完整源代码: https ://developers.google.com/gmail/api/quickstart/nodejs

const fs = require('fs').promises;
const path = require('path');
const process = require('process');
const {authenticate} = require('@google-cloud/local-auth');
const {google} = require('googleapis');

const OAuth2 = google.auth.OAuth2;
const REDIRECT_URI = ["http://localhost"]
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.compose'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = path.join(process.cwd(), 'token.json');
const CREDENTIALS_PATH = path.join(process.cwd(), 'google_credentials.json');

const nodemailer = require("nodemailer");
const MailComposer = require("nodemailer/lib/mail-composer");

async function loadSavedCredentialsIfExist() {
   try {
       const content = await fs.readFile(TOKEN_PATH);
       const credentials = JSON.parse(content);
       return google.auth.fromJSON(credentials);
   } catch (err) {
     return null;
   }
}

async function saveCredentials(client) {
   const content = await fs.readFile(CREDENTIALS_PATH);
   const keys = JSON.parse(content);
   const key = keys.installed || keys.web;
   const payload = JSON.stringify({
      type: 'authorized_user',
      client_id: key.client_id,
      client_secret: key.client_secret,
      refresh_token: client.credentials.refresh_token,
   });
   await fs.writeFile(TOKEN_PATH, payload);
   }

   async function authorize() {
   let client = await loadSavedCredentialsIfExist();
  if (client) {
    return client;
  }
 client = await authenticate({
   scopes: SCOPES,
   keyfilePath: CREDENTIALS_PATH,
});
 if (client.credentials) {
   await saveCredentials(client);
  }
   return client;
}

let EMAIL_ADDRESS = process.env.GMAIL_EMAIL_ADDRESS

function streamToString (stream) {
  const chunks = [];
  return new Promise((resolve, reject) => {
    stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
    stream.on('error', (err) => reject(err));
    stream.on('end', () =>     resolve(Buffer.concat(chunks).toString('utf8')));
  })
 }

 let messagePayload = {
    subject: "Exploring Gmail API",
    text: "Hi, this is a test email from Node.js using Gmail API",
    to: "email@gmail.com",
    from: EMAIL_ADDRESS,
    attachments: [{filename: 'doc.pdf', path: './doc.pdf'}]
  }

  async function sendEmail(auth) {
    const gmail = google.gmail({version: 'v1', auth});
    var mail = new MailComposer(messagePayload);
    var stream = mail.compile().createReadStream();
    const messageResult = await streamToString(stream)
    const encodedMessage = Buffer.from(messageResult).toString('base64')
   .replace(/\+/g, '-')
   .replace(/\//g, '_')
   .replace(/=+$/, '');

   const res = await gmail.users.messages.send({
       userId: 'me',
      requestBody: {
        raw: encodedMessage,
      },
    });
    console.log(res.data);
     return res.data;
    }

   authorize().then(sendEmail).catch(console.error);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM