Vert.x 邮件客户端 (SMTP 客户端实现)

Vert.x 客户端,用于通过本地邮件服务器(例如 postfix)或外部邮件服务器(例如 googlemail 或 aol)发送 SMTP 电子邮件。

该客户端支持一些额外的认证方法,如 DIGEST-MD5,并完全支持 TLS 和 SSL,且是完全异步的。客户端支持连接池,以保持连接开放以供重用。

要使用此项目,请将以下依赖项添加到您的构建描述符的依赖项部分

  • Maven(在您的 pom.xml 中)

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-mail-client</artifactId>
  <version>5.0.1</version>
</dependency>
  • Gradle(在您的 build.gradle 文件中)

compile 'io.vertx:vertx-mail-client:5.0.1'

创建客户端

您可以通过创建一个客户端来发送邮件,该客户端会从本地 JVM 打开 SMTP 连接。

客户端使用一个配置对象,默认配置被创建为空对象,并将连接到 localhost 的端口 25,这在运行 Postfix 或类似邮件服务器的本地机器上的标准 Linux 环境中应该没问题。有关配置对象的所有可能属性,请参见下文。

客户端可以使用 SMTP 连接的连接池,以避免每次连接到服务器、协商 TLS 和登录的开销(此功能可以通过设置 `keepAlive = false` 来关闭)。客户端可以是共享的或非共享的,如果是共享的,所有使用相同标识符的客户端将使用相同的连接池。

MailConfig config = new MailConfig();
MailClient mailClient = MailClient
  .createShared(vertx, config, "exampleclient");

对 `MailClient.createShared` 的首次调用将实际使用指定的配置创建连接池。后续调用将返回一个新的客户端实例,该实例使用相同的连接池,因此配置将不会被使用。

如果您省略连接池标识符,将创建一个默认连接池。请注意,客户端仅在 Vert.x 实例的范围内共享(因此两个不同的 Vert.x 将拥有具有相同标识符的不同连接池)。

非共享客户端可以通过同样的方式创建,省略标识符即可。

MailConfig config = new MailConfig();
MailClient mailClient = MailClient.create(vertx, config);

使用需要通过 TLS 登录的邮件服务器的更详细示例

MailConfig config = new MailConfig();
config.setHostname("mail.example.com");
config.setPort(587);
config.setStarttls(StartTLSOptions.REQUIRED);
config.setUsername("user");
config.setPassword("password");
MailClient mailClient = MailClient.create(vertx, config);

发送邮件

一旦客户端对象创建成功,您就可以使用它来发送邮件。由于邮件发送在 Vert.x 中是异步工作的,因此当邮件操作完成时,结果处理器将被调用。您可以并行启动多个邮件发送操作,连接池将限制并发操作的数量,以便在没有可用插槽时新操作将在队列中等待。

邮件消息以 JSON 形式构建。`MailMessage` 对象具有 `from`、`to`、`cc`、`bcc`、`subject`、`text`、`html` 等属性。根据设置的值,生成的 MIME 消息的格式将有所不同。收件人地址属性可以是单个地址或地址列表。

MIME 编码器支持 us-ascii (7bit) 头部/消息和 utf8 (通常为 quoted-printable) 头部/消息

MailMessage message = new MailMessage();
message.setFrom("[email protected] (Example User)");
message.setTo("[email protected]");
message.setCc("Another User <[email protected]>");
message.setText("this is the plain message text");
message.setHtml("this is html text <a href=\"https://vertx.com.cn\">vertx.io</a>");

附件可以通过 `MailAttachment` 对象使用存储在 `Buffer` 中的数据来创建,这支持 base64 附件。

MailAttachment attachment = MailAttachment.create();
attachment.setContentType("text/plain");
attachment.setData(Buffer.buffer("attachment file"));

message.setAttachment(attachment);

当使用内联附件(通常是图片)时,可以在 HTML 消息中引用图片,以便在邮件中显示包含图片的 HTML。图片可以在 HTML 文本中引用为 ``,相应的图片具有 `Disposition: inline` 和 `Content-ID` 头部,格式为 `""`。请注意,RFC 2392 要求 `Content-ID` 值结构类似于 `Message-ID`,带有尖括号和使用 URL 兼容编码的本地及域部分。这些都没有强制执行,并且大多数邮件客户端支持没有尖括号或没有域部分的 ID,最佳实践是使用严格格式。`Content-ID` 值的一个有效示例如 `"[email protected]>" `

MailAttachment attachment = MailAttachment.create();
attachment.setContentType("image/jpeg");
attachment.setData(Buffer.buffer("image data"));
attachment.setDisposition("inline");
attachment.setContentId("<[email protected]>");

message.setInlineAttachment(attachment);

发送邮件时,您可以提供一个 `AsyncResult` 处理器,它将在发送操作完成或失败时被调用。

邮件发送方式如下

mailClient.sendMail(message)
  .onSuccess(System.out::println)
  .onFailure(Throwable::printStackTrace);

DKIM 签名邮件

它支持 DomainKeys Identified Mail (DKIM) 签名以保护您的电子邮件。您只需指定所需的配置即可为您的电子邮件签名。

启用 DKIM 功能的邮件客户端可以按如下方式创建

DKIMSignOptions dkimSignOptions = new DKIMSignOptions();
dkimSignOptions.setPrivateKey("PKCS8 Private Key Base64 String");
dkimSignOptions.setAuid("[email protected]");
dkimSignOptions.setSelector("selector");
dkimSignOptions.setSdid("example.com");
MailConfig config = new MailConfig()
  .setDKIMSignOption(dkimSignOptions)
  .setEnableDKIM(true);

MailClient mailClient = MailClient.createShared(vertx, config);

邮件客户端创建后,每次 mailClient.sendMail 调用都会通过添加额外的 DKIM-Signature 头部来为电子邮件签名。

缓存 DKIM 中使用的附件流

为了执行 DKIM 签名,需要对电子邮件正文(包括附件)进行哈希。如果附件来自 `ReadStream`,它将无法再次读取。因此我们需要缓存附件数据。该客户端提供了两种缓存策略。

  • 内存缓存

默认情况下,流内容会缓存在内存中,以便稍后发送。

  • 缓存到临时文件

您可以通过指定系统属性 vertx.mail.attachment.cache.filetrue 来将附件流中的数据缓存到临时文件中,适用于大型附件。每次发送后,它会尝试删除临时文件。

邮件客户端数据对象

`MailMessage` 属性

电子邮件字段是字符串,使用电子邮件的常见格式,带或不带真实姓名

`MailMessage` 对象具有以下属性

  • from 表示发件人地址和 `MAIL FROM` 字段的字符串

  • to 表示收件人地址和 `RCPT TO` 字段的字符串或字符串列表

  • cc 同 `to`

  • bcc 同 `to`

  • bounceAddress 表示错误地址 (`MAIL FROM`) 的字符串,如果未设置则使用 `from`

  • text 表示邮件 `text/plain` 部分的字符串

  • html 表示邮件 `text/html` 部分的字符串

  • attachment 邮件附件 (`MailAttachment`) 或消息附件列表 (`MailAttachment` 列表)

  • inlineAttachment 邮件内联附件 (`MailAttachment`) 或消息内联附件列表 (`MailAttachment` 列表) (通常是图片)

  • headers 表示除了 MIME 消息所需的头部之外要添加的头部的 `MultiMap`

  • fixedHeaders 布尔值,如果为 `true`,则生成的邮件中将只设置作为 `headers` 属性提供的头部

最后两个属性允许使用自定义头部操作生成的消息,例如提供由调用程序选择的 message-id 或设置与默认生成不同的头部。除非您知道自己在做什么,否则这可能会生成无效消息。

MailAttachment 属性

`MailAttachment` 对象具有以下属性

  • data 包含附件二进制数据的 `Buffer`

  • stream 表示附件二进制数据源的 `ReadStream`

  • size 整数,描述当使用 `stream` 作为二进制数据源时附件的大小

  • contentType 附件的 `Content-Type` 字符串(例如 `text/plain` 或 `text/plain; charset="UTF8"`,默认值为 `application/octet-stream`)

  • description 描述附件的字符串(此内容放置在附件的 description 头部中),可选

  • disposition 描述附件处置方式的字符串(可以是 "inline" 或 "attachment",默认是 `attachment`)

  • name 附件的文件名字符串(此内容放置在附件的 disposition 和 `Content-Type` 头部中),可选

  • contentId 描述附件 `Content-Id` 的字符串(用于识别内联图片),可选

  • headers 除了默认头部之外的附件头部 `MultiMap`,可选

MailConfig 选项

配置具有以下属性

  • hostname 要连接的 SMTP 服务器主机名(默认是 localhost)

  • port 要连接的 SMTP 服务器端口(默认是 25)

  • startTLS `StartTLSOptions`,可以是 `DISABLED`、`OPTIONAL` 或 `REQUIRED`,默认是 `OPTIONAL`

  • login `LoginOption`,可以是 `DISABLED`、`NONE` 或 `REQUIRED`,默认是 `NONE`

  • username 用于登录的用户名字符串(仅当 `LoginOption` 为 `REQUIRED` 时需要)

  • password 用于登录的密码字符串(仅当 `LoginOption` 为 `REQUIRED` 时需要)

  • ssl 布尔值,表示连接到邮件服务器时是否使用 SSL(默认是 `false`),设置为 `true` 可使用 465 端口的 SSL 连接(默认是 `false`)

  • ehloHostname 用于 EHLO 和创建 message-id 的字符串,如果未设置,则使用自己的主机名,如果它不包含 FQDN 或为 localhost,则可能不是一个好的选择(可选)

  • authMethods 允许的认证方法的字符串,以空格分隔的列表,可用于禁止某些认证方法或定义一个必需的认证方法(可选)

  • keepAlive 布尔值,如果连接池已启用(默认是 `true`)

  • maxPoolSize 整数,连接池中保持的最大开放连接数或一次性打开的最大连接数(无论是否启用连接池),默认是 10

  • trustAll 布尔值,是否接受服务器的所有证书(默认是 `false`)

  • keyStore 密钥库文件名字符串,可用于信任自定义生成的服务器证书(可选)

  • keyStorePassword 用于解密密钥库的密码字符串(可选)

  • allowRcptErrors 布尔值,如果为 `true`,则当收件人地址不被接受时发送会继续,并且如果至少一个地址被接受,邮件仍将发送(默认是 `false`)

  • disableEsmtp 布尔值,如果为 `true`,将不使用 ESMTP 相关命令(如果您的 SMTP 服务器甚至不为 EHLO 命令提供正确的错误响应代码,则设置此项)(默认是 `false`)

  • userAgent 字符串,表示用于生成多部分电子邮件边界和 message-id 的邮件用户代理 (MUA) 名称,默认是 vertxmail

  • enableDKIM 布尔值,如果为 `true`,则当也设置了 DKIM 配置时,DKIM 签名将被启用,默认是 `false`。

  • dkimSignOptions `DKIMSignOptions` 列表,用于执行 DKIM 签名。

  • pipelining 如果 SMTP 服务器支持,则启用管道化。默认是 true

  • multiPartOnly 布尔值,是否仅将邮件消息编码为多部分。默认是 false

  • poolCleanerPeriod 整数,连接池清理器周期(毫秒)。默认是 1000 ms

  • poolCleanerPeriodUnit 清理连接池的时间单位,默认是 TimeUnit.MILLISECONDS

  • keepAliveTimeout 整数,SMTP 连接的保持活动超时时间(秒)。默认是 300 s

  • keepAliveTimeoutUnit 保持连接池中连接活动的时间单位。默认是 TimeUnit.SECONDS

  • ntDomain 字符串,用于 NTLM 认证的域名。如果 username 遵循 <DOMAIN>\<UserName> 格式,则 \ 之前的部分将用作域名。

  • workstation 字符串,用于 NTLM 认证的工作站名称

  • maxMailsPerConnection 长整数,每个连接在关闭前可发送的最大邮件数量

MailResult 对象

`MailResult` 对象具有以下成员

  • messageID 生成邮件的 `Message-ID`

  • recipients 邮件发送到的收件人列表(如果 `allowRcptErrors` 为 `true`,此列表可能少于预期的收件人)

DKIMSignOptions 对象

`DKIMSignOptions` 对象具有以下属性

  • privateKey 用于签署电子邮件的 RSA PKCS#8 格式私钥。

  • privateKeyPath 指定 RSA PKCS#8 格式私钥的文件路径。privateKeyprivateKeyPath 必须设置其中之一。

  • signAlgo `DKIMSignAlgorithm.RSA_SHA256`(默认)或 `DKIMSignAlgorithm.RSA_SHA1`。用于正文哈希和签名算法。

  • signedHeaders 字符串列表,指定将用于执行签名的电子邮件头部。默认值:FromReply-toSubjectDateToCc。注意:顺序很重要。

  • sdid 必需,字符串,签名域标识符 (SDID),通常是 SMTP 服务器的域。

  • auid 可选,字符串,代理或用户标识符 (AUID),默认是 `@` 加 sdid

  • selector 必需,字符串,用于查询公钥的选择器。

  • headerCanonAlgo 用于邮件头部的规范化算法,可以是 simple(默认)或 relaxed

  • bodyCanonAlgo 用于邮件正文的规范化算法,可以是 simple(默认)或 relaxed

  • bodyLimit 可选,整数,用于计算正文哈希的正文长度。

  • signatureTimestamp 可选,布尔值,是否在 DKIM-Signature 标签列表中包含时间戳。默认是 `false`

  • expireTime 可选,长整数,从现在起签名将过期的时间(秒)。

  • copiedHeaders 可选,字符串列表,DKIM 中使用的复制头部。通常根据 DKIM 规范用于调试目的。