回复带有附件的主题(邮件)的线程(邮件),将其作为发件人帐户中的新邮件发送-Gmail API [英] Reply to a thread (mail) with attachment being sent as a new mail in sender account - gmail api
问题描述
我正在尝试使用gmail api发送对带有附件的线程(邮件)的回复.但是,它是作为新邮件在发件人帐户中发送的,但是在收件人帐户中却是在同一线程中看到的.对于发送方和接收方,此邮件也应位于同一线程中.
I am trying to send reply to a thread (mail) with attachment using gmail api. But it is being sent as a new mail in senders account, but in receiver account it is seen in same thread. This mail should be in same thread for both sender and receiver sides as well.
我正在使用node js和request-promise模块来实现这一目标. 这是我的代码.请看看,让我知道我哪里错了.
I am using node js and request-promise module for achieving this. Here is my piece of code. Please have a look and let me know where I am wrong.
let from = req.body.email;
let mailString = '';
for (let i in req.files) {
mailString += '--boundary_mail1\n'
mailString += `Content-Type: ${req.files[i].mimetype}\n`
mailString += `Content-Disposition: attachment; filename="${req.files[i].filename}"\n`
mailString += "Content-Transfer-Encoding: base64\n\n"
mailString += `${fs.readFileSync(req.files[i].path).toString('base64')}` + '\n'
}
let raw = [
'MIME-Version: 1.0\n',
"to: ", reply.mailContent.to, "\n",
"from: ", from, "\n",
"threadId: ", reply.mailContent.id, "\n",
"References: ", reply.mailContent.references, "\n",
"In-Reply-To: ", reply.mailContent.messageId, "\n",
"subject: ", reply.mailContent.subject, "\n",
"Content-Type: multipart/mixed; boundary=boundary_mail1\n\n",
"--boundary_mail1\n",
"Content-Type: multipart/alternative; boundary=boundary_mail2\n\n",
"--boundary_mail2\n",
"Content-Type: text/html; charset=UTF-8\n",
"Content-Transfer-Encoding: quoted-printable\n\n",
req.body.message = req.body.message ? req.body.message : '', "\n\n",
"--boundary_mail2--\n",
mailString,
'--boundary_mail1--',
].join('');
const id = 'me';
let options = {
url: "https://www.googleapis.com/upload/gmail/v1/users/" + id + "/messages/send?uploadType=multipart",
method: 'POST',
headers: {
'Authorization': `Bearer ${user.options.access_token}`,
'Content-Type': 'message/rfc822'
},
body: raw
};
await request(options).then(async b => {
return res.apiOk();
}).catch(err => {
return res.apiError(err);
})
}).catch(err => {
return res.apiError("Something went wrong!");
})
推荐答案
解释它的位置有点复杂.
It's a bit complicated to explain how should it be.
I had the same problem, after a while, I ended up this js-function to create a Gmail-Message-Body. It works fine now.
让我们深入了解
Gmail正文制作工具
function createJson(threadId) {
const thread = threadId ? `"threadId": "${threadId}"\r\n` : ''
return [
'Content-Type: application/json; charset="UTF-8"\r\n\r\n',
`{\r\n${thread}}`
].join('')
}
function createHeaders(headers) {
if ( !headers || headers.length === 0 )
return ''
const result = []
for (const h in headers) {
if (headers.hasOwnProperty(h)) {
result.push(`${h}: ${headers[h]}\r\n`)
}
}
return result.join('')
}
function createPlain(text) {
return [
'Content-Type: text/plain; charset="UTF-8"\r\n',
'MIME-Version: 1.0\r\n',
'Content-Transfer-Encoding: 7bit\r\n\r\n',
text
].join('')
}
function createHtml(html) {
return [
'Content-Type: text/html; charset="UTF-8"\r\n',
'MIME-Version: 1.0\r\n',
'Content-Transfer-Encoding: 7bit\r\n\r\n',
html
].join('')
}
function createAlternative(text, html) {
return [
'Content-Type: multipart/alternative; boundary="foo"\r\n\r\n',
'--foo\r\n',
createPlain(text),
'\r\n\r\n',
'--foo\r\n',
createHtml(html),
'\r\n\r\n',
'--foo--',
].join('')
}
function createBody(text, html) {
if (text && html)
return createAlternative(text, html)
if (text)
return createPlain(text)
if (html)
return createHtml(html)
return ''
}
function createAttachments(attachments) {
if ( !attachments || attachments.length === 0 )
return ''
const result = []
for (let i = 0; i < attachments.length; i++) {
const att = attachments[i]
const attName = att.filename ? `; filename="${att.filename}"` : ''
// Content-Type: image/jpeg
// MIME-Version: 1.0
// Content-ID: <20180619202303.24365.655.img@domain>
// Content-Transfer-Encoding: base64
// Content-Disposition: inline
result.push('--foo_bar\r\n')
result.push(`Content-Type: ${att.type}\r\n`)
result.push('MIME-Version: 1.0\r\n')
if (att.contentId) result.push(`Content-ID: <${att.contentId}>\r\n`)
result.push('Content-Transfer-Encoding: base64\r\n')
result.push(`Content-Disposition: attachment${attName}\r\n\r\n`)
result.push(`${att.content}\r\n\r\n`)
}
return result.join('')
}
function gmailBodyMaker(params) {
const json = createJson(params.threadId)
const headers = createHeaders(params.headers)
const body = createBody(params.text, params.html)
const attachments = createAttachments(params.attachments)
return [
'--foo_bar_baz\r\n',
`${json}\r\n\r\n`,
'--foo_bar_baz\r\n',
'Content-Type: message/rfc822\r\n\r\n',
'Content-Type: multipart/mixed; boundary="foo_bar"\r\n',
`${headers}\r\n`,
'--foo_bar\r\n',
`${body}\r\n\r\n`,
attachments,
'--foo_bar--\r\n\r\n',
'--foo_bar_baz--',
].join('')
}
创建电子邮件对象
const createEmailObject = async function (params) {
const attachments = []
for (const att of params.attachments) {
const imageData = await request.get({
url: att.link,
encoding: 'binary'
})
const base64 = Buffer.from(imageData, 'binary').toString('base64')
const size = base64.length // in bytes
if ( size > 5242880)
throw Error.BadRequest('File-size could not be greater than 4-MB!')
attachments.push({
'filename': att.filename,
'contentId': att.contentId || null, // <img src="cid:some-image-cid" alt="img" />
'content': base64,
'encoding': 'base64',
'type': att.type
})
}
const toRecipients = params.header.to ? params.header.to.map(record => (record.address)) : []
const ccRecipients = params.header.cc ? params.header.cc.map(record => (record.address)) : []
const bccRecipients = params.header.bcc ? params.header.bcc.map(record => (record.address)) : []
return {
headers: {
From: params.header.from,
To: toRecipients.join(','),
Cc: ccRecipients.join(','),
Bcc: bccRecipients.join(','),
'In-Reply-To': params.header['In-Reply-To'],
Subject: params.header.subject
},
threadId: params.threadId,
attachments: attachments,
text: params.body.text,
html: params.body.html
}
}
电子邮件发件人
// Highly recommend to use this method
Google.prototype.sendMultipartMessage = async function (email) {
// File Size Limit: Up To 35 MB
const authHeader = await this.oAuth2Client.getRequestHeaders()
const arr = authHeader.Authorization.split('Bearer ')
const token = arr[1]
const responseString = await request.post({
url: 'https://www.googleapis.com/upload/gmail/v1/users/me/messages/send?uploadType=multipart',
headers: {
'Authorization': `OAuth ${token}`,
'Content-Type': 'multipart/related; boundary="foo_bar_baz"'
},
body: gmailBodyMaker(email)
})
return JSON.parse(responseString)
}
// Alternative Method
Google.prototype.sendMessage = async function (email) {
const message = [
'Content-Type: text/plain; charset="UTF-8"',
'MIME-Version: 1.0',
'Content-Transfer-Encoding: 7bit',
`To: ${email.to}`,
`Subject: ${email.subject}`,
'', // Extra new line required
`${email.htmlBody}`
].join('\n')
const raw = Buffer.from(message).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const response = await this.gmail.users.messages.send({
auth: this.oAuth2Client,
userId: this.gmailAddress,
resource: {
threadId: email.threadId,
raw: raw
}
})
return response
}
// Alternative Method
Google.prototype.sendMessageWithAttachment = async function (email) {
// File Size Limit: 5 MB
const message = new MailComposer({
to: email.to,
subject: email.subject,
text: email.textBody,
html: email.htmlBody,
textEncoding: 'base64',
attachments: email.attachments
})
const builtMessage = await message.compile().build()
const raw = Buffer.from(builtMessage).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const response = await this.gmail.users.messages.send({
auth: this.oAuth2Client,
userId: this.gmailAddress,
uploadType: 'multipart',
resource: {
threadId: email.threadId,
raw: raw
}
})
return response
}
主脚本
const params = {
'header': {
'subject': 'your-subject',
'from': 'name <email@domain.com> ',
'to': [{'address': 'email_2@domain.com', 'name': 'name'}],
'cc': [{'address': 'email_3@domain.com', 'name': 'name'}],
'bcc': [{'address': 'email_4@domain.com', 'name': 'name'}],
'In-Reply-To': `<any_valid_internet_message_id_goes_here>` || null
},
'threadId': 'any_valid_thread_id_goes_here' || null,
'attachments': [
{
'filename': 'filename.jpg',
'link': 'https://some-where.com/my_image.jpg',
'type': 'image/jpeg',
'isInline': true,
'contentId': 'any_valid_content_id'
}
],
'body': {
'text': 'text',
'html': 'html'
}
}
// Solution 1
const body = await createEmailObject(params)
const result = await google.sendMultipartMessage(body)
const sentMessageId = result.id
这篇关于回复带有附件的主题(邮件)的线程(邮件),将其作为发件人帐户中的新邮件发送-Gmail API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!