จะส่งอีเมลพร้อมไฟล์แนบโดยใช้ Gmail API ใน 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')
        }
    });
}

person moxched    schedule 27.02.2018    source แหล่งที่มา


คำตอบ (3)


มีคำแนะนำเกี่ยวกับเรื่องนี้ในการสร้างข้อความพร้อมไฟล์แนบ:

การสร้างข้อความพร้อมไฟล์แนบก็เหมือนกับการสร้างข้อความอื่นๆ แต่ขั้นตอนการอัปโหลดไฟล์เป็นข้อความ MIME แบบหลายส่วนจะขึ้นอยู่กับภาษาการเขียนโปรแกรม

สำหรับการอ้างอิงตัวอย่าง NodeJS ให้ตรวจสอบ โพสต์ SO

person noogui    schedule 01.03.2018
comment
โพสต์ SO ที่คุณอ้างอิงนั้นไร้ประโยชน์โดยสิ้นเชิง มันไม่ได้แสดงวิธีการทำ แต่มันบอกว่ามองหาตัวคุณเอง - person Joris Mans; 10.06.2018

อาจจะช้าไปสักหน่อย อย่างไรก็ตาม ฉันจะใช้เวลาในกรณีที่มีคนต้องการทางเลือกอื่นในภายหลัง

ปัญหาสำคัญของแนวทาง Moxched ก็คือเขาอาจจำเป็นต้องพิจารณาข้อมูลจำเพาะ MIME ให้ละเอียดยิ่งขึ้น (ซึ่งเป็นปัญหาใหญ่สำหรับฉัน) เพื่อทำความเข้าใจบางสิ่งที่จำเป็นในการส่งไฟล์แนบให้ดีขึ้น

จากจุดที่ฉันยืนเพื่อให้สามารถใช้ gmail API เพื่อส่งไฟล์แนบและสิ่งอื่น ๆ อีกมากมายคุณต้องสร้างคำขอทั้งหมดตามข้อกำหนด MIME เพื่อทำเช่นนั้นคุณต้องเข้าใจว่าสิ่งต่าง ๆ ใน MIME ทำงานอย่างไรรวมถึงขอบเขตด้วย

วิธี Joris ใช้งานได้ แต่สุดท้ายไม่ได้ใช้ nodeJS lib เพื่อส่งอีเมล เหตุผลที่เขาไม่สามารถใช้คำตอบจากแพ็คเกจ gmail-api-create-message-body กับ gmail API ได้เนื่องจากด้วยเหตุผลบางอย่าง lib นี้จึงสร้างที่ด้านบนของข้อความ 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 ที่สร้างขึ้นเข้ากันได้กับ 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: "[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 อยู่นอกขอบเขตสำหรับคำตอบนี้

ต่อไปเราต้องส่งอีเมลจริงๆ เป็นไปไม่ได้สำหรับฉันที่จะทำให้มันทำงานกับ Gmail api อย่างเป็นทางการ (รับ 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
โย่ Joris เฮ้เพื่อน ฉันเห็นว่าคุณเป็นผู้พัฒนาเกมคนก่อน และดูเหมือนว่าตอนนี้คุณอยู่ผู้พัฒนาเว็บแล้ว ฉันสามารถเพิ่มคุณ (twitter, อีเมล, ฯลฯ ) ได้ไหม? อยากทราบเคล็ดลับในสาขานั้น ฉันเป็นผู้พัฒนาเกม 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