Как отправить электронное письмо с вложением с помощью Gmail API в Node.js?

Я новичок в Node.js и пытаюсь создать почтовый ящик с помощью API Gmail, все работает нормально, за исключением загрузки вложения по электронной почте. Я нашел примеры с 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')
        }
    });
}

person moxched    schedule 27.02.2018    source источник


Ответы (3)


Инструкции по этому поводу есть в Создание сообщений с вложениями:

Создание сообщения с вложением похоже на создание любого другого сообщения, но процесс загрузки файла в виде сообщения MIME, состоящего из нескольких частей, зависит от языка программирования.

Для справки по образцу NodeJS проверьте этот Опубликовать.

person noogui    schedule 01.03.2018
comment
Сообщение SO, на которое вы ссылаетесь, совершенно бесполезно. Он не показывает, как это сделать, он просто говорит, ищите сами - person Joris Mans; 10.06.2018

Это может немного опоздать, в любом случае я потрачу время на случай, если кто-то позже захочет альтернативу.

Основная проблема с подходом Moxched заключалась в том, что, вероятно, ему нужно было более внимательно изучить спецификацию MIME (что было для меня большой проблемой), чтобы лучше понять несколько вещей, которые необходимы для отправки вложений.

С моей точки зрения, чтобы иметь возможность использовать API gmail для отправки вложений и многого другого, вам нужно построить весь запрос в соответствии со спецификацией MIME, для этого вам нужно понять, как работают вещи в MIME, включая границы.

Подход Joris работает, но в итоге не использует библиотеку nodeJS для отправки электронной почты. Причина, по которой он не смог использовать ответ из пакета gmail-api-create-message-body с API gmail, заключается в том, что по какой-то причине эта библиотека генерирует в верхней части своего сообщения MIME следующее:

'Content-Type: multipart/related; boundary="foo_bar_baz"',
`In-Reply-To: [email protected]`,
`References: `,
`From: [email protected]`,
`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-body и mail-composer от nodemailer.

Используя nodemailer/lib/mail-composer, вы сможете с легкостью генерировать необходимое MIME-сообщение в соответствии со спецификацией MIME, оно включает в себя поддержку вложений и множество других вещей. Сгенерированные сообщения MIME совместимы с API Gmail. Я оставляю рабочий пример, основанный на примерах документов NodeJS, который отправляет электронное письмо с двумя вложениями.

Надеюсь это поможет!

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: "[email protected]",
      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----------------------------------------------------


}

person LPS    schedule 13.09.2018
comment
так как я разместил этот вопрос, мой проект был приостановлен, ваш ответ очень полезен, я продолжу работу над ним, спасибо - person moxched; 13.09.2018
comment
@moxched, нет проблем, если вам понравилось или было полезно, вы можете проголосовать :) - person LPS; 14.09.2018
comment
Это должен быть принятый ответ. Даже если подход с использованием gmail-api-create-message-body сработал для меня, это не самое лучшее, что вы теряете возможность отправлять электронную почту с помощью API (например, вы не получаете идентификатор электронной почты, который у вас только что был отправлен назад). Использование nodemailer — это путь, так как создание всего тела сообщения с нуля громоздко и подвержено ошибкам. Не забудьте также добавить свойство contentType во вложения. - person Hernan Veiras; 04.04.2019
comment
Не стесняйтесь голосовать за него или принимать его, как вы можете себе представить, у меня нет проблем с этим :) Спасибо. - person LPS; 08.04.2019

Застряв на одной и той же проблеме, мне удалось найти решение, хватаясь за вещи слева и справа.

вам нужно использовать пакет 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()

Я думаю, что полная информация о том, как использовать OAuth2 с Google, выходит за рамки этого ответа.

Далее нам нужно отправить почту. Я не мог заставить его работать с официальным API Gmail (продолжал получать 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
});

И это все работало отлично.

person Joris Mans    schedule 10.06.2018
comment
Если бы я хотел ударить в стиле SO, я бы попросил, чтобы этот вопрос был помечен как дубликат со ссылкой на вопрос, который вы разместили: D - person Joris Mans; 11.06.2018
comment
йо, Джорис, эй, чувак, я вижу, ты раньше был разработчиком игр, а теперь, похоже, ты занимаешься веб-разработкой. Могу ли я добавить вас (твиттер, электронная почта и т. д.)? хотелось бы узнать несколько советов в этой области. Я немного разрабатываю 2D-игры, а также изучаю веб-разработку. - person noogui; 11.06.2018
comment
Я ни в коем случае не гуру веб-разработки. Моя основная специализация — iOS, но уже пару лет я делаю Nodejs для бэкенда и React для фронтенда. Все эти геймдевы давно ушли в прошлое. С 2000 по 2007 :) - person Joris Mans; 11.06.2018
comment
хороший человек ... я тоже иду по пути NodeJS и React (в настоящее время VueJS). позволь мне добавить тебя - person noogui; 11.06.2018