node.js 基于 STMP 协议和 EWS 协议发送邮件


Posted in Javascript onFebruary 14, 2021

本文主要介绍 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://lzw.me" rel="external nofollow" rel="external nofollow" >https://lzw.me</a>`,
 /** TEXT 文本格式邮件正文内容 */
 text: '',
 to: 'xxx@lzw.me',
 // 附件列表
 // attachments: [],
};
const smtpConfig = {
 host: 'smtp.qq.com', //QQ: smtp.qq.com; 网易: smtp.163.com
 port: 465, //端口号。QQ邮箱 465,网易邮箱 25
 secure: true,
 auth: {
 user: 'xxx@qq.com', //邮箱账号
 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: 'abc@xxx.com',
 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://lzw.me" rel="external nofollow" rel="external nofollow" >https://lzw.me</a>`,
 /** TEXT 文本格式邮件正文内容(优先级低于 html 参数) */
 text: '',
 to: 'xxx@lzw.me',
})

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-…

以上就是node.js 基于 STMP 协议和 EWS 协议发送邮件的详细内容,更多关于node.js 发送邮件的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
通过JAVAScript实现页面自适应
Jan 19 Javascript
js中top的作用深入剖析
Mar 04 Javascript
js字符串完全替换函数分享
Dec 03 Javascript
Javascript基础教程之if条件语句
Jan 18 Javascript
js操作数组函数实例小结
Dec 10 Javascript
Bootstrap CSS使用方法
Dec 23 Javascript
Bootstrap导航条鼠标悬停下拉菜单
Jan 04 Javascript
bootstrap3-dialog-master模态框使用详解
Aug 22 Javascript
Vue2.0用 watch 观察 prop 变化(不触发)
Sep 08 Javascript
JS实现简单的星期格式转换功能示例
Jul 23 Javascript
vue实现输入框的模糊查询的示例代码(节流函数的应用场景)
Sep 01 Javascript
JS如何在数组指定位置插入元素
Mar 10 Javascript
如何在 Vue 中使用 JSX
Feb 14 #Vue.js
Vue单页面应用中实现Markdown渲染
Feb 14 #Vue.js
vue仿携程轮播图效果(滑动轮播,下方高度自适应)
Feb 11 #Vue.js
详解ES6实现类的私有变量的几种写法
Feb 10 #Javascript
微信小程序实现点赞业务
Feb 10 #Javascript
JS获取一个字符串中指定字符串第n次出现的位置
Feb 10 #Javascript
JavaScript canvas实现跟随鼠标移动小球
Feb 09 #Javascript
You might like
Windows下PHP的任意文件执行漏洞
2006/10/09 PHP
php 文件上传实例代码
2012/04/19 PHP
Linux系统下php获得系统分区信息的方法
2015/03/30 PHP
PHP连接数据库实现注册页面的增删改查操作
2016/03/27 PHP
PHP内部实现打乱字符串顺序函数str_shuffle的方法
2019/02/14 PHP
一起来写段JS drag拖动代码
2010/12/09 Javascript
图片上传插件jquery.uploadify详解
2013/11/15 Javascript
jquery实现省市select下拉框的替换(示例代码)
2014/02/22 Javascript
jquery实现翻动fadeIn显示的方法
2015/03/05 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
2015/12/31 Javascript
前端js弹出框组件使用方法
2020/08/24 Javascript
详解node.js搭建代理服务器请求数据
2017/04/08 Javascript
最适应的vue.js的form提交涉及多种插件【推荐】
2018/08/27 Javascript
解决layer弹出层msg的文字不显示的问题
2019/09/11 Javascript
使用layer.msg 时间设置不起作用的解决方法
2019/09/12 Javascript
layui prompt 设置允许空白提交的方法
2019/09/24 Javascript
JavaScript undefined及null区别实例解析
2020/07/21 Javascript
Python图像灰度变换及图像数组操作
2016/01/27 Python
python 矩阵增加一行或一列的实例
2018/04/04 Python
Python3实现爬取指定百度贴吧页面并保存页面数据生成本地文档的方法
2018/04/22 Python
Python切片索引用法示例
2018/05/15 Python
python实现人民币大写转换
2018/06/20 Python
在Python中append以及extend返回None的例子
2019/07/20 Python
TensorFlow实现checkpoint文件转换为pb文件
2020/02/10 Python
通过实例解析python subprocess模块原理及用法
2020/10/10 Python
详解三种方式实现平滑滚动页面到顶部的功能
2019/04/23 HTML / CSS
德国EGOIST网店:销售畅销的设计师品牌
2017/04/18 全球购物
Hunkemöller西班牙:欧洲最大的内衣连锁店
2018/08/15 全球购物
Roxy俄罗斯官方网站:冲浪和滑雪板的一切
2020/06/20 全球购物
酒店保洁主管岗位职责
2013/11/28 职场文书
业务主管岗位职责范本
2013/12/25 职场文书
函授毕业个人自我评价
2014/02/20 职场文书
湖南省召开党的群众路线教育实践活动总结大会报告
2014/10/21 职场文书
小时代观后感
2015/06/10 职场文书
奶茶店的创业计划书该怎么写?
2019/07/15 职场文书
python实现简单的井字棋
2021/05/26 Python