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 相关文章推荐
jQuery Form 页面表单提交的小例子
Nov 15 Javascript
jquery全选checkBox功能实现代码(取消全选功能)
Dec 10 Javascript
javascript实现当前页导航激活的方法
Feb 27 Javascript
js实现点击链接后窗口缩小并居中的方法
Mar 02 Javascript
全面解析Bootstrap中Carousel轮播的使用方法
Jun 13 Javascript
js addDqmForPP给标签内属性值加上双引号的函数
Dec 24 Javascript
jquery 判断是否支持Placeholder属性的方法
Feb 07 Javascript
es6的数字处理的方法(5个)
Mar 16 Javascript
JS原生数据双向绑定实现代码
Aug 14 Javascript
layui数据表格重载实现往后台传参
Nov 15 Javascript
Vue + Element-ui的下拉框el-select获取额外参数详解
Aug 14 Javascript
jQuery实现推拉门效果
Oct 19 jQuery
如何在 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
解析php做推送服务端实现ios消息推送
2013/07/01 PHP
ThinkPHP实现将本地文件打包成zip下载
2014/06/26 PHP
PHP中ltrim与rtrim去除左右空格及特殊字符实例
2016/01/07 PHP
php通过执行CutyCapt命令实现网页截图的方法
2016/09/30 PHP
JavaScript 新手24条实用建议[TUTS+]
2009/06/21 Javascript
JavaScript DOM 学习第九章 选取范围的介绍
2010/02/19 Javascript
Javascript开发之三数组对象实例介绍
2012/11/12 Javascript
可在线编辑网页文字效果代码(单击)
2013/03/02 Javascript
JS实现的省份级联实例代码
2013/06/24 Javascript
js实现div在页面拖动效果
2016/05/04 Javascript
AngularJs Modules详解及示例代码
2016/09/01 Javascript
JS判断是否为JSON对象及是否存在某字段的方法(推荐)
2016/11/29 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
微信小程序实现多个按钮的颜色状态转换
2019/02/15 Javascript
JavaScript的级联函数用法简单示例【链式调用】
2019/03/26 Javascript
vue学习笔记之Vue中css动画原理简单示例
2020/02/29 Javascript
javascript实现搜索筛选功能实例代码
2020/11/12 Javascript
Python中除法使用的注意事项
2014/08/21 Python
python线程、进程和协程详解
2016/07/19 Python
Python实现列表删除重复元素的三种常用方法分析
2017/11/24 Python
python http接口自动化脚本详解
2018/01/02 Python
Python实现判断字符串中包含某个字符的判断函数示例
2018/01/08 Python
python将回车作为输入内容的实例
2018/06/23 Python
python实现顺序表的简单代码
2018/09/28 Python
Python爬取商家联系电话以及各种数据的方法
2018/11/10 Python
Django 使用easy_thumbnails压缩上传的图片方法
2019/07/26 Python
瑞士图书网站:Weltbild.ch
2019/09/17 全球购物
优秀毕业大学生推荐信
2013/11/13 职场文书
经典公益广告词
2014/03/13 职场文书
档案保密承诺书
2014/06/03 职场文书
法学专业求职信
2014/07/15 职场文书
励志演讲稿800字
2014/08/21 职场文书
2014年电工工作总结
2014/11/20 职场文书
2015年手术室工作总结
2015/05/11 职场文书
redis内存空间效率问题的深入探究
2021/05/17 Redis
Win11显卡控制面板打开显卡设置方法
2022/04/20 数码科技