這篇文章主要介紹了node.js 基于 STMP 協議和 EWS 協議發送郵件的示例,幫助大家更好的理解和使用node.js,感興趣的朋友可以了解下
本文主要介紹 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', // path: path.resolve(__dirname, 'pic01.jpg') // }, { // filename: 'test.txt', // path: path.resolve(__dirname, '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://www.zztuku.com" rel="external nofollow" rel="external nofollow" >https://www.zztuku.com</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, msg: '', 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, ' response:', 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', /** HTML 格式郵件正文內容 */ html: `email content for test: <a href="https://www.zztuku.com" rel="external nofollow" rel="external nofollow" >https://www.zztuku.com</a>`, /** TEXT 文本格式郵件正文內容(優先級低于 html 參數) */ text: '', to: '[email protected]', })
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, msg: '', 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'; } 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-…