最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用 node.js 发送基于 STMP 与 MS Exchange 邮件的方法

    正文概述 掘金(任侠)   2021-02-14   430

    本文主要介绍 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">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-ewsews-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">https://lzw.me</a>`,
        /** TEXT 文本格式邮件正文内容(优先级低于 html 参数) */
        text: '',
        to: 'xxx@lzw.me',
    })
    

    2.1.2 基于 NTLMAuth 的认证配置方式

    直接配置 pass 密码可能会导致明文密码泄露,我们可以将 pass 字段留空,配置 nt_passwordlm_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-…

    本文首发于志文工作室,转载请注明来源:lzw.me/a/nodejs-st…


    起源地下载网 » 使用 node.js 发送基于 STMP 与 MS Exchange 邮件的方法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元