1. 程式人生 > 程式設計 >node.js 基於 STMP 協議和 EWS 協議傳送郵件

node.js 基於 STMP 協議和 EWS 協議傳送郵件

本文主要介紹 node.js 傳送基於 STMP 協議和 MS Exchange Web Service(EWS) 協議的郵件的方法。文中所有參考程式碼均以 TypeScript 編碼示例。

1 基於 STMP 協議的 node.js 傳送郵件方法

提到使用 node.js 傳送郵件,基本都會提到大名鼎鼎的 Nodemailer 模組,它是當前使用 STMP 方式傳送郵件的首選。
基於 NodeMailer 傳送 STMP 協議郵件的文章網上已非常多,官方文件介紹也比較詳細,在此僅列舉示例程式碼以供對比參考:
封裝一個 sendMail 郵件傳送方法:

/**
 * 使用 Nodemailer 傳送 STMP 郵件
 * @param {Object} opts 郵件傳送配置
 * @param {Object} smtpCfg smtp 伺服器配置
 */
async function sendMail(opts,smtpCfg) {
 const resultInfo = { code: 0,msg: '',result: null };
 if (!smtpCfg) {
 resultInfo.msg = '未配置郵件傳送資訊';
 resultInfo.code = - 1009;
 return resultInfo;
 }

 // 建立一個郵件物件
 const mailOpts = Object.assign(
 {
 // 發件人
 from: `Notify <${smtpCfg.auth.user}>`,// 主題
 subject: 'Notify',// text: opts.content,// html: opts.content,// 附件內容
 // /*attachments: [{
 // filename: 'data1.json',// path: path.resolve(__dirname,'data1.json')
 // },{
 // filename: 'pic01.jpg','pic01.jpg')
 // },{
 // filename: 'test.txt','test.txt')
 // }],*/
 },opts
 );

 if (!mailOpts.to) mailOpts.to = [];
 if (!Array.isArray(mailOpts.to)) mailOpts.to = String(mailOpts.to).split(',');
 mailOpts.to = mailOpts.to.map(m => String(m).trim()).filter(m => m.includes('@'));

 if (!mailOpts.to.length) {
 resultInfo.msg = '未配置郵件接收者';
 resultInfo.code = - 1010;
 return resultInfo;
 }

 const mailToList = mailOpts.to;
 const transporter = nodemailer.createTransport(smtpCfg);

 // to 列表分開發送
 for (const to of mailToList) {
 mailOpts.to = to.trim();
 try {
 const info = await transporter.sendMail(mailOpts);
 console.log('mail sent to:',mailOpts.to,' response:',info.response);
 resultInfo.msg = info.response;
 } catch (error) {
 console.log(error);
 resultInfo.code = -1001;
 resultInfo.msg = error;
 }
 }

 return resultInfo;
}

使用 sendMail 方法傳送郵件:

const opts = {
 subject: 'subject for test',/** HTML 格式郵件正文內容 */
 html: `email content for test: <a href="https://lzw.me" rel="external nofollow" rel="external nofollow" >https://lzw.me</a>`,/** TEXT 文字格式郵件正文內容 */
 text: '',to: '[email protected]',// 附件列表
 // attachments: [],};
const smtpConfig = {
 host: 'smtp.qq.com',//QQ: smtp.qq.com; 網易: smtp.163.com
 port: 465,//埠號。QQ郵箱 465,網易郵箱 25
 secure: true,auth: {
 user: '[email protected]',//郵箱賬號
 pass: '',//郵箱的授權碼
 },};
sendMail(opts,smtpConfig).then(result => console.log(result));

2 基於 MS Exchange 郵件伺服器的 node.js 傳送郵件方法

對於使用微軟的 Microsoft Exchange Server 搭建的郵件服務,Nodemailer 就無能為力了。Exchange Web Service(EWS)提供了訪問 Exchange 資源的介面,在微軟官方文件中對其有詳細的介面定義文件。針對 Exchange 郵件服務的流行第三方庫主要有 node-ews 和 ews-javascript-api。

2.1 使用 node-ews 傳送 MS Exchange 郵件

下面以 node-ews 模組為例,介紹使用 Exchange 郵件服務傳送郵件的方法。

2.1.1 封裝一個基於 node-ews 傳送郵件的方法

封裝一個 sendMailByNodeEws 方法:

import EWS from 'node-ews';

export interface IEwsSendOptions {
 auth: {
 user: string;
 pass?: string;
 /** 密碼加密後的祕鑰(NTLMAuth.nt_password)。為字串時,應為 hex 編碼結果 */
 nt_password?: string | Buffer;
 /** 密碼加密後的祕鑰(NTLMAuth.lm_password)。為字串時,應為 hex 編碼結果 */
 lm_password?: string | Buffer;
 };
 /** Exchange 地址 */
 host?: string;
 /** 郵件主題 */
 subject?: string;
 /** HTML 格式郵件正文內容 */
 html?: string;
 /** TEXT 文字格式郵件正文內容(優先順序低於 html 引數) */
 text?: string;
 to?: string;
}

/**
 * 使用 Exchange(EWS) 傳送郵件
 */
export async function sendMailByNodeEws(options: IEwsSendOptions) {
 const resultInfo = { code: 0,result: null };

 if (!options) {
 resultInfo.code = -1001;
 resultInfo.msg = 'Options can not be null';
 } else if (!options.auth) {
 resultInfo.code = -1002;
 resultInfo.msg = 'Options.auth{user,pass} can not be null';
 } else if (!options.auth.user || (!options.auth.pass && !options.auth.lm_password)) {
 resultInfo.code = -1003;
 resultInfo.msg = 'Options.auth.user or Options.auth.password can not be null';
 }

 if (resultInfo.code) return resultInfo;

 const ewsConfig = {
 username: options.auth.user,password: options.auth.pass,nt_password: options.auth.nt_password,lm_password: options.auth.lm_password,host: options.host,// auth: 'basic',};

 if (ewsConfig.nt_password && typeof ewsConfig.nt_password === 'string') {
 ewsConfig.nt_password = Buffer.from(ewsConfig.nt_password,'hex');
 }

 if (ewsConfig.lm_password && typeof ewsConfig.lm_password === 'string') {
 ewsConfig.lm_password = Buffer.from(ewsConfig.lm_password,'hex');
 }

 Object.keys(ewsConfig).forEach(key => {
 if (!ewsConfig[key]) delete ewsConfig[key];
 });

 // initialize node-ews
 const ews = new EWS(ewsConfig);
 // define ews api function
 const ewsFunction = 'CreateItem';
 // define ews api function args
 const ewsArgs = {
 attributes: {
  MessageDisposition: 'SendAndSaveCopy',},SavedItemFolderId: {
  DistinguishedFolderId: {
  attributes: {
   Id: 'sentitems',Items: {
  Message: {
  ItemClass: 'IPM.Note',Subject: options.subject,Body: {
   attributes: {
   BodyType: options.html ? 'HTML' : 'Text',$value: options.html || options.text,ToRecipients: {
   Mailbox: {
   EmailAddress: options.to,IsRead: 'false',};

 try {
 const result = await ews.run(ewsFunction,ewsArgs);
 // console.log('mail sent to:',options.to,result);
 resultInfo.result = result;
 if (result.ResponseMessages.MessageText) resultInfo.msg = result.ResponseMessages.MessageText;
 } catch (err) {
 console.log(err.stack);
 resultInfo.code = 1001;
 resultInfo.msg = err.stack;
 }

 return resultInfo;
}

使用 sendMailByNodeEws 方法傳送郵件:

sendMailByNodeEws({
 auth: {
 user: '[email protected]',pass: '123456',/** 密碼加密後的祕鑰(NTLMAuth.nt_password)。為字串時,應為 hex 編碼結果 */
 nt_password: '',/** 密碼加密後的祕鑰(NTLMAuth.lm_password)。為字串時,應為 hex 編碼結果 */
 lm_password: '',/** Exchange 地址 */
 host: 'https://ews.xxx.com',/** 郵件主題 */
 subject: 'subject for test',/** TEXT 文字格式郵件正文內容(優先順序低於 html 引數) */
 text: '',})

2.1.2 基於 NTLMAuth 的認證配置方式

直接配置 pass 密碼可能會導致明文密碼洩露,我們可以將 pass 欄位留空,配置 nt_password 和 lm_password 欄位,使用 NTLMAuth 認證模式。此二欄位基於 pass 明文生成,其 nodejs 生成方式可藉助 httpntlm 模組完成,具體參考如下:

import { ntlm as NTLMAuth } from 'httpntlm';

/** 將輸入的郵箱賬號密碼轉換為 NTLMAuth 祕鑰(hex)格式並輸出 */
const getHashedPwd = () => {
 const passwordPlainText = process.argv.slice(2)[0];

 if (!passwordPlainText) {
 console.log('USEAGE: \n\tnode get-hashed-pwd.js [password]');
 return;
 }

 const nt_password = NTLMAuth.create_NT_hashed_password(passwordPlainText.trim());
 const lm_password = NTLMAuth.create_LM_hashed_password(passwordPlainText.trim());

 // console.log('\n password:',passwordPlainText);
 console.log(` nt_password:`,nt_password.toString('hex'));
 console.log(` lm_password:`,lm_password.toString('hex'));

 return {
 nt_password,lm_password,};
};

getHashedPwd();

2.2 使用 ews-javascript-api 傳送 MS Exchange 郵件

基於 ews-javascript-api 傳送郵件的方式,在其官方 wiki 有相關示例,但本人在測試過程中未能成功,具體為無法取得伺服器認證,也未能查證具體原因,故以下程式碼僅作參考:

/**
 * 使用 `ews-javascript-api` 傳送(MS Exchange)郵件
 */
export async function sendMailByEwsJApi(options: IEwsSendOptions) {
 const resultInfo = { code: 0,pass} can not be null';
 } else if (!options.auth.user || (!options.auth.pass && !options.auth.lm_password)) {
 resultInfo.code = -1003;
 resultInfo.msg = 'Options.auth.user or Options.auth.password can not be null';
 }

 const ews = require('ews-javascript-api');
 const exch = new ews.ExchangeService(ews.ExchangeVersion.Exchange2010);
 exch.Credentials = new ews.WebCredentials(options.auth.user,options.auth.pass);
 exch.Url = new ews.Uri(options.host);
 ews.EwsLogging.DebugLogEnabled = true; // false to turnoff debugging.

 const msgattach = new ews.EmailMessage(exch);
 msgattach.Subject = options.subject;
 msgattach.Body = new ews.MessageBody(ews.BodyType.HTML,escape(options.html || options.text));
 if (!Array.isArray(options.to)) options.to = [options.to];
 options.to.forEach(to => msgattach.ToRecipients.Add(to));
 // msgattach.Importance = ews.Importance.High;

 // 傳送附件
 // msgattach.Attachments.AddFileAttachment('filename to attach.txt','c29tZSB0ZXh0');

 try {
 const result = await msgattach.SendAndSaveCopy(); // .Send();
 console.log('DONE!',result);
 resultInfo.result = result;
 } catch (err) {
 console.log('ERROR:',err);
 resultInfo.code = 1001;
 resultInfo.msg = err;
 }
 return resultInfo;
}

3 擴充套件參考

nodemailer.com/about/
github.com/CumberlandG…
github.com/gautamsi/ew…
github.com/lzwme/node-…

以上就是node.js 基於 STMP 協議和 EWS 協議傳送郵件的詳細內容,更多關於node.js 傳送郵件的資料請關注我們其它相關文章!