如何在 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')
}
});
}
Creating messages with attachments中有关于此的说明:
创建带附件的邮件与创建任何其他邮件一样,但将文件上传为 multi-part MIME 邮件的过程取决于编程语言。
对于 NodeJS 示例参考,请选中此 SO Post。
被困在同一个问题上,我设法通过左右抓取东西来构建解决方案。
你需要用到的是npm包gmail-api-create-message-body
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
[=21= 的示例所示]
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
});
这一切都很完美。
这可能会有点晚,无论如何我会花时间以防万一有人以后想要一个替代方案。
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-body
和 [=14 的回复=] 来自 nodemailer.
使用 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----------------------------------------------------
}
我是 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')
}
});
}
Creating messages with attachments中有关于此的说明:
创建带附件的邮件与创建任何其他邮件一样,但将文件上传为 multi-part MIME 邮件的过程取决于编程语言。
对于 NodeJS 示例参考,请选中此 SO Post。
被困在同一个问题上,我设法通过左右抓取东西来构建解决方案。
你需要用到的是npm包gmail-api-create-message-body
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
[=21= 的示例所示]
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
});
这一切都很完美。
这可能会有点晚,无论如何我会花时间以防万一有人以后想要一个替代方案。
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-body
和 [=14 的回复=] 来自 nodemailer.
使用 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----------------------------------------------------
}