回复带有附件的主题(邮件)的线程(邮件),将其作为发件人帐户中的新邮件发送-Gmail API [英] Reply to a thread (mail) with attachment being sent as a new mail in sender account - gmail api

查看:128
本文介绍了回复带有附件的主题(邮件)的线程(邮件),将其作为发件人帐户中的新邮件发送-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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆