如何使用 nodejs 下载 Gmail API 的附件?

Posted

技术标签:

【中文标题】如何使用 nodejs 下载 Gmail API 的附件?【英文标题】:How do you download an attachment with Gmail API with nodejs? 【发布时间】:2018-12-15 10:01:39 【问题描述】:

我正在尝试在我的本地计算机上创建一个节点应用程序来执行此操作:

1) 查看我的 gmail 收件箱。

2) 如果邮件有特定的标签和附件,请下载。

我在第 2 步。

我困惑的部分是关于“部分”的部分。

https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get

谷歌示例代码:

function getAttachments(userId, message, callback) 
  var parts = message.payload.parts;
  for (var i = 0; i < parts.length; i++) 
    var part = parts[i];
    if (part.filename && part.filename.length > 0) 
      var attachId = part.body.attachmentId;
      var request = gapi.client.gmail.users.messages.attachments.get(
        'id': attachId,
        'messageId': message.id,
        'userId': userId
      );
      request.execute(function(attachment)  // <--- that attachment param
        callback(part.filename, part.mimeType, attachment);
      );
    
  

“附件”似乎包含附件数据。

这是我的代码:

const gmail = google.gmail(version: 'v1', auth);

gmail.users.messages.list(
  userId: 'me',
  'q':'subject:my-test has:nouserlabels has:attachment'
, (err, res) => 
  if (err) return console.log('1 The API returned an error: ' + err);

  const msgs = res.data.messages;

  if(typeof msgs !== 'undefined')
    console.log('Messages:');
    msgs.forEach( msg => 

      console.log(`- $msg.id`);

      gmail.users.messages.get(
        userId: 'me',
        id: msg.id,
        format:'full'
      ,(err, res) => 
        if (err) return console.log('2 The API returned an error: ' + err);

        // Wheres the data????? //

        console.log('A')
        console.log(res.data.payload.parts)

        console.log('B')
        console.log(res.data.payload.parts[1])

        console.log('C')
        console.log(res.data.payload.parts[1].body)
      )
    );
   else 
    console.log('no messages found')
  


);

【问题讨论】:

如果这真的是谷歌官方的例子,他们可能会看看“循环内部的闭包”......但是无论如何,你的问题是什么?! 基本上,如何使用 nodejs 从 gmail 下载附件。我已经给出了我在上面尝试过的内容。 【参考方案1】:

您可以在每封邮件中使用part.body.attachmentId 使用gmail.users.messages.attachments.get 进行Gmail API 调用。

下面是一个函数,它返回一个带有附件大小和 base64 编码附件的 JSON 对象。我意识到它现在有点乱,我还在打磨它。我留下了console.logs,以防您可能想进一步检查这些对象。您应该也可以将您的 gmail.users.messages.list 查询添加到我的函数中。

从这里,您也许可以使用 npm 包保存附件文件,例如 https://www.npmjs.com/package/base64-to-image。

function listMessagesWithAttachments(auth, callback) 
    const gmail = google.gmail(version: 'v1', auth)
    console.log('list messages with attachments auth: ' + JSON.stringify(auth))
    gmail.users.messages.list(
      userId: 'me',
      maxResults: 5
    , (err, data) => 
      if (err) return console.log('Error in listMessageswithAttachments, messages list: ' + err)
      let dataForExpress = []
      let messages = data.messages
      messages.forEach(function (message, index) 
        gmail.users.messages.get(
          userId: 'me',
          id: data.messages[index].id,
          format: 'full'
        , (err, data) => 
          if (err) return console.log('Error: ' + err)
            //console.log('data to push: ' + data)
            //console.log('messages.length: ' + messages.length)
            dataForExpress.push(data)
            if (dataForExpress.length == messages.length) 
              let attachmentIds = []
              let attachments = []
                dataForExpress.forEach(function(message, index) 
                    //console.log('message number: ' + index)
                    //console.log(message.payload.parts)
                    let messagePayloadParts = message.payload.parts 
                    if (messagePayloadParts.length > 0) 
                        messagePayloadParts.forEach(function (part, j) 
                        //console.log('messageId: ' + JSON.stringify(message.id))
                        //console.log('message parts partId ' + JSON.stringify(part.partId))
                        //console.log('mime-type ' + JSON.stringify(part.mimeType))
                        //console.log('filename ' + JSON.stringify(part.filename))
                            let object = 
                            if (part.body.size && part.body.size > 0 && part.body.attachmentId) 
                                //console.log('messageId: ' + JSON.stringify(part.partId))
                                //console.log('attachmentId ' + JSON.stringify(part.body.attachmentId))
                                let object = "index": `$j`, "messageId": message.id, "attachmentId": part.body.attachmentId
                                attachmentIds.push(object)
                            
                            if (dataForExpress.length - 1 == index) 
                                attachmentIds.forEach(function (attachment, kindex) 
                                    gmail.users.messages.attachments.get(
                                        userId: 'me',
                                        messageId: attachment.messageId,
                                        id: attachment.attachmentId
                                    , (err, data) => 
                                        if (err) return console.log('Error getting attachment: ' + err)
                                        //console.log('attachment' + JSON.stringify(data))
                                        attachments.push(data)
                                        if (attachmentIds.length == attachments.length)
                                        console.log('callback: ' + JSON.stringify(attachments))
                                        callback(attachments)
                                        
                                    )
                                )   
                            
                        )                               
                                  
                )
            
        )
        )
    )

【讨论】:

【参考方案2】:

我刚刚用它从我最喜欢的会计师那里检索文档。

const fsPromises = require('fs/promises');
const path = require('path');
const google = require('googleapis');
const readline = require('readline');
const fs = require('fs');
 
.... code from https://developers.google.com/gmail/api/quickstart/nodejs for auth

const fromEmail = process.argv[2];
const shortFromEmail = fromEmail.substring(0, fromEmail.indexOf('@'));
/**
 * Lists the labels in the user's account.
 *
 * @param google.auth.OAuth2 auth An authorized OAuth2 client.
 */
async function main(auth) 
  const gmail = google.gmail(version: 'v1', auth)
//  console.log('list messages with attachments auth: ' + JSON.stringify(auth))
  const data = await gmail.users.messages.list(
    userId: 'me',
    'q':`from:$fromEmail has:nouserlabels has:attachment`,
//    maxResults: 5
  );
  console.log(data);
  let messages = data;
  await messages.reduce(async function (accProm, messageId) 
    await accProm;
    const data: message = await gmail.users.messages.get(
      userId: 'me',
      id: messageId.id,
      format: 'full'
    );
//    console.log(JSON.stringify(message, null, 2));
    let messagePayloadParts = message.payload.parts 
    const attachments = await messagePayloadParts.reduce(async function (acc2Prom, part) 
      const acc2 = await acc2Prom;
      if (!part.body.size || part.body.attachmentId == undefined) 
        return acc2;
      
      const internalDate = new Date(parseInt(message.internalDate, 10));
      const datestring = internalDate.getFullYear() + " " + (internalDate.getMonth()+1).toString().padStart(2, "0") + " " + internalDate.getDate().toString().padStart(2, "0");
      console.log(datestring, shortFromEmail, part.filename, part)
      let fileExt;
      switch (part.mimeType) 
      case 'application/octet-stream': fileExt = ''; break;
      case 'application/pdf': fileExt = 'pdf'; break;
      case 'message/rfc822': fileExt = 'eml'; break;
      case 'image/jpeg': fileExt = 'jpg'; break;
      case 'image/png': fileExt = 'png'; break;
      case 'application/msword': fileExt = 'doc'; break;
      default: console.error('unknownMimeType', part.mimeType); boom;
      
      const dirname = `$datestring $shortFromEmail`;
      const filename = part.filename ? part.filename : `noname_$part.body.attachmentId.substring(0,8).$fileExt`
      const attachmentPath = path.join(dirname, filename);
      const s = await fsPromises.stat(attachmentPath).catch(e => undefined);
      if (s)  return acc2 ;
      const data: attachment = await gmail.users.messages.attachments.get(
        userId: 'me',
        messageId: message.id,
        id: part.body.attachmentId,
      );
      const  size, data: dataB64  = attachment;
      await fsPromises.mkdir(dirname, recursive: true);
      await fsPromises.writeFile(attachmentPath, Buffer.from(dataB64, 'base64'));
      return acc2;
 //   console.log('callback: ' + JSON.stringify(attachments))
    , Promise.resolve([]));
  , Promise.resolve());

【讨论】:

以上是关于如何使用 nodejs 下载 Gmail API 的附件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Nodejs 中使用 gmail api 在同一对话中回复电子邮件

Gmail NodeJs客户端中的Gmail API userId用例?

如何使用 Gmail API 在 nodejs 服务器中连接我的 G-Suite

NodeJS:无法使用服务帐户访问 Gmail API

NodeJS 中的 API Gmail:(节点:14592)UnhandledPromiseRejectionWarning:错误:委派被拒绝

尝试实现 NodeJS Gmail API 示例