当前位置: 首页 > news >正文

【Rust发邮件】Rust如何通过smtp协议发送邮件

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Rust开发,Python全栈,Golang开发,云原生开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Rust语言通关之路
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • Rust发邮件
    • 1. 前言
    • 2. SMTP协议简介
      • 2.1 SMTP基本概念
      • 2.2 SMTP基本命令集
      • 2.3 邮件封装
    • 3. Rust 中的邮件库 lettre
      • 3.1 简介
      • 3.2 安装依赖
    • 4. 发送邮件的基本步骤
      • 4.1 使用126邮箱发送邮件
      • 4.2 发送html格式的邮件
      • 4.3 发送带附件的邮件
      • 4.4 安全建议

Rust发邮件

1. 前言

在现代软件开发中,邮件发送功能是许多应用程序不可或缺的一部分。无论是用户注册验证、密码重置、通知提醒还是营销推广,邮件服务都扮演着重要角色。Rust作为一门系统级编程语言,以其安全性、并发性和高性能著称,同样可以用于实现邮件发送功能。

本文将详细介绍如何使用 Rust 通过 SMTP 协议发送邮件,使用主流库 lettre,并涵盖:
SMTP 协议基础
安装与配置 lettre 库
构建邮件内容(text/html)
TLS 与身份验证
完整代码示例(包括 Gmail、QQ 邮箱等)
错误处理与调试技巧

2. SMTP协议简介

2.1 SMTP基本概念

SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是用来传输电子邮件的协议,主要用于系统之间的邮件信息传递,并提供有关来信的通知。
使用SMTP,可实现相同网络处理进程之间的邮件传输,也可通过中继器或网关实现某处理进程与其它网络之间的邮件传输。
SMTP工作在两种情况下,一种是电子邮件从客户机传输到服务器,另外一种是从某个服务器传输到另外一个服务器(即中继)。
SMTP是请求/响应协议,命令和响应都是基于ASCII文本,并且以CR和LF符结尾,响应包括一个表示返回状态的三位数字代码。
SMTP(Simple Mail Transfer Protocol)是邮件传输的标准协议。它使用 TCP 通信,默认端口包括:
25:非加密(已逐渐废弃)
465:使用 SSL/TLS 加密
587:使用 STARTTLS 加密(推荐)

2.2 SMTP基本命令集

在这里插入图片描述

SMTP应答码
在这里插入图片描述

2.3 邮件封装

SMTP将传输的内容封装在对象中,邮件对象主要包括信封和内容两部分。内容又分为头部和邮件内容本身
在这里插入图片描述

SMTP协议在传输报文时,只能够传输7位的ASCI格式的报文,不支持不使用7位ASCI格式的语种,也不支持语音和视频数据的传输,因此需要扩展协议MIME来解决此问题。
MIME协议定义了5种头部,加在原始STMP头部,如下:
在这里插入图片描述

发送邮件流程如下:
建立 TCP 连接
TLS 握手(STARTTLS 或 SSL)
身份验证(如 LOGIN、PLAIN)
发送邮件头和正文
结束会话

3. Rust 中的邮件库 lettre

3.1 简介

lettre 是 Rust 社区维护的一个成熟邮件发送库,支持多种 SMTP 模式、身份验证、HTML 邮件等。
在这里插入图片描述

3.2 安装依赖

编辑你的 Cargo.toml 添加如下依赖:

[dependencies]
anyhow = "1.0.98"
lettre = { version = "0.11", features = ["builder","smtp-transport","rustls-tls","tokio1-rustls","tokio1-native-tls",
] }
tokio = { version = "1", features = ["full"] }

在这里插入图片描述

使用 tokio 异步运行时
rustls-tls 为安全加密库的实现(比 OpenSSL 更轻量)

4. 发送邮件的基本步骤

4.1 使用126邮箱发送邮件

✅ 126 邮箱 SMTP 信息
在这里插入图片描述

use lettre::message::{ header, Mailbox, Message, SinglePart };
use lettre::transport::smtp::authentication::Credentials;
use lettre::{ SmtpTransport, Transport };//使用anyhow处理错误
#[tokio::main]
async fn main() -> anyhow::Result<()> {// 你的126邮箱账号和授权码let email_address = "ldddddd1@126.com";let password = "NVTtB33ggsgsgsSvB"; // 注意是授权码而不是登录密码// 构建邮件内容let email = Message::builder().from(Mailbox::new(Some("Rust 邮件测试".to_string()), email_address.parse()?)).to("34224572@qq.com".parse()?).subject("来自 Rust 的邮件测试 - 126邮箱").singlepart(SinglePart::builder().header(header::ContentType::TEXT_PLAIN) // 指定邮件内容类型为纯文本.body(String::from("你好!这是一封由 Rust 通过 126 邮箱发出的测试邮件。")))?;// 凭证配置let creds = Credentials::new(email_address.to_string(), password.to_string());// 创建支持 SMTPS (SSL on port 465) 的邮件客户端let mailer = SmtpTransport::relay("smtp.126.com")? // 自动使用 SMTPS.port(465).credentials(creds).build();// 发送邮件match mailer.send(&email) {Ok(_) => println!("✅ 邮件发送成功!"),Err(e) => println!("❌ 邮件发送失败: {:?}", e),}Ok(())
}

在这里插入图片描述

4.2 发送html格式的邮件

header::ContentType有两种格式
一种是纯文本,一种是html格式
在这里插入图片描述

use lettre::message::{ header, Mailbox, Message, SinglePart };
use lettre::transport::smtp::authentication::Credentials;
use lettre::{ SmtpTransport, Transport };//使用anyhow处理错误
#[tokio::main]
async fn main() -> anyhow::Result<()> {// 你的126邮箱账号和授权码let email_address = "lifsfsfs324242@126.com";let password = "NVTt424rggsdBSvB"; // 注意是授权码而不是登录密码// 构建邮件内容let email = Message::builder().from(Mailbox::new(Some("Rust 邮件测试".to_string()), email_address.parse()?)).to("243342572@qq.com".parse()?).subject("来自 Rust 的邮件测试 - 126邮箱").singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML) // 指定邮件内容类型为 HTML.body(String::from("<h2>你好!这是一封由 Rust 通过 126 邮箱发出的测试邮件。</h2>")))?;// 凭证配置let creds = Credentials::new(email_address.to_string(), password.to_string());// 创建支持 SMTPS (SSL on port 465) 的邮件客户端let mailer = SmtpTransport::relay("smtp.126.com")? // 自动使用 SMTPS.port(465).credentials(creds).build();// 发送邮件match mailer.send(&email) {Ok(_) => println!("✅ 邮件发送成功!"),Err(e) => println!("❌ 邮件发送失败: {:?}", e),}Ok(())
}

4.3 发送带附件的邮件

lettre_email 是一个旧版的 lettre 附加库,可以用于发送带附件的邮件。它基于早期版本的 lettre,提供更方便的 API 来构建包含文本、HTML 以及附件的电子邮件。
⚠️ 注意事项
lettre_email 不再积极维护,只支持旧版本 lettre(<= 0.9)
与 lettre 0.10+(目前主流版本)不兼容
对于附件发送,可以考虑使用lettre::Message来添加附件

✅ 替代方案(现代 lettre::Message)
要使用 lettre::Message(推荐方式)发送带附件的邮件,需要:
构造标准的 multipart/mixed MIME 邮件;
附件使用 Base64 编码并嵌入;
设置适当的 Content-Type、Content-Disposition、Content-Transfer-Encoding。

在使用 lettre::Message 构建附件时,不需要你手动进行 Base64 编码!lettre 会 自动编码附件内容,只要你提供原始字节,并正确设置 ContentTransferEncoding::Base64 即可。

🧪MIME 类型参考
在这里插入图片描述

可以使用mime_guess::from_path 自动识别mime类型
附件支持的格式(自动识别)
你可以传入任意类型的文件,mime_guess 会自动识别 MIME 类型,如:
.pdf → application/pdf
.png → image/png
.zip → application/zip
.txt → text/plain

✅ 示例:使用 lettre::Message 发送带附件邮件
📦 Cargo.toml

[dependencies]
anyhow = "1.0.98"
base64 = "0.22.1"
lettre = { version = "0.11", features = ["builder","smtp-transport","rustls-tls","tokio1-rustls","tokio1-native-tls",
] }
mime = "0.3.17"
mime_guess = "2.0.5"
tokio = { version = "1", features = ["full"] }

🦀 完整代码

use lettre::{message::{ header::ContentType, Mailbox, Message, MultiPart, SinglePart, header },transport::smtp::authentication::Credentials,SmtpTransport,Transport,
};
use anyhow::Result;
use std::{ fs, path::Path };
use mime_guess::from_path;fn main() -> Result<()> {let from = "lifsfs424@126.com";let to = "412313170@qq.com";let subject = "看看Kitty漂不漂亮";let body = "Hello, Kitty!";let file_path = "kitty.jpg";// 构建邮件正文let text_part = SinglePart::builder().header(ContentType::TEXT_PLAIN).body(String::from(body));// 构建附件部分let attachment = build_attachment_part(file_path)?;// 构建整个 multipartlet multipart = MultiPart::mixed().singlepart(text_part).singlepart(attachment);// 构建邮件let email = Message::builder().from(from.parse::<Mailbox>()?).to(to.parse::<Mailbox>()?).subject(subject).multipart(multipart)?;// 配置 SMTPlet creds = Credentials::new(from.into(), "NVvsvvSvB".into());let mailer = SmtpTransport::relay("smtp.126.com")?.credentials(creds).port(465).build();mailer.send(&email)?;println!("✅ 邮件发送成功");Ok(())
}//构建附件部分
fn build_attachment_part(path: &str) -> Result<SinglePart> {let data = fs::read(path)?;let filename = Path::new(path).file_name().unwrap().to_string_lossy().to_string();//根据文件后缀获取mime类型let mime = from_path(path).first_or_octet_stream();//构建附件部分Ok(SinglePart::builder().header(header::ContentType::parse(&format!("{}; name=\"{}\"", mime, filename))?).header(header::ContentDisposition::attachment(&filename.clone())).header(header::ContentTransferEncoding::Base64).body(data))
}

📌 关键点说明
在这里插入图片描述

📁 支持多个附件?
只需 .singlepart() 多次调用:

let multipart = MultiPart::mixed().singlepart(text_part).singlepart(build_attachment_part("a.pdf")?).singlepart(build_attachment_part("b.png")?);

多个附件完整代码:
//多个附件

use lettre::{message::{ header::ContentType, Mailbox, Message, MultiPart, SinglePart, header },transport::smtp::authentication::Credentials,SmtpTransport,Transport,
};
use anyhow::Result;
use std::{ fs, path::Path };
use mime_guess::from_path;fn main() -> Result<()> {let from = "3141fsf1@126.com";let to = "ffsfsfsf70@qq.com";let subject = "看看Kitty漂不漂亮";let body = "Hello, Kitty!";let file_path = "kitty.jpg";let file_path2 = "kitty2.jpg";// 构建邮件正文let text_part = SinglePart::builder().header(ContentType::TEXT_PLAIN).body(String::from(body));// 构建整个 multipart//发送多个附件let multipart = MultiPart::mixed().singlepart(text_part).singlepart(build_attachment_part(file_path)?).singlepart(build_attachment_part(file_path2)?);// 构建邮件let email = Message::builder().from(from.parse::<Mailbox>()?).to(to.parse::<Mailbox>()?).subject(subject).multipart(multipart)?;// 配置 SMTPlet creds = Credentials::new(from.into(), "fsfsfKfsf".into());let mailer = SmtpTransport::relay("smtp.126.com")?.credentials(creds).port(465).build();mailer.send(&email)?;println!("✅ 邮件发送成功");Ok(())
}//构建附件部分
fn build_attachment_part(path: &str) -> Result<SinglePart> {let data = fs::read(path)?;let filename = Path::new(path).file_name().unwrap().to_string_lossy().to_string();//根据文件后缀获取mime类型let mime = from_path(path).first_or_octet_stream();//构建附件部分Ok(SinglePart::builder().header(header::ContentType::parse(&format!("{}; name=\"{}\"", mime, filename))?).header(header::ContentDisposition::attachment(&filename.clone())).header(header::ContentTransferEncoding::Base64).body(data))
}

多个收件人:
.to(“收件人1 a@example.com”.parse()?)
.to(“收件人2 b@example.com”.parse()?)
在这里插入图片描述

📌 总结:构建附件的 3 要素
在这里插入图片描述

4.4 安全建议

切勿将密码硬编码在源码中,应使用 .env 或配置文件
dotenvy::dotenv() 自动加载 .env 文件
env::var(“KEY”) 从环境中读取变量

创建.env
放在项目根目录:
EMAIL_ADDRESS = “3131fsfs@126.com”
PASSWORD = “NVdasfsds”

注意:.env 文件不要提交到 Git,建议添加 .gitignore:
.env

使用 dotenvy 读取环境变量:
[dependencies]
dotenvy = “0.15.7”

应用代码

use lettre::message::{ header, Mailbox, Message, SinglePart };
use lettre::transport::smtp::authentication::Credentials;
use lettre::{ SmtpTransport, Transport };use dotenvy::dotenv;
use std::env;//使用anyhow处理错误
#[tokio::main]
async fn main() -> anyhow::Result<()> {// 从 .env 文件中加载环境变量dotenv()?;// 从.env读取126邮箱账号和授权码let email_address = env::var("EMAIL_ADDRESS")?;let password = env::var("PASSWORD")?;// 构建邮件内容let email = Message::builder().from(Mailbox::new(Some("Rust 邮件测试".to_string()), email_address.parse()?)).to("64745772@qq.com".parse()?).to("6464hao@t64464c.cn".parse()?) //多个收件人.subject("来自 Rust 的邮件测试 - 126邮箱").singlepart(SinglePart::builder().header(header::ContentType::TEXT_HTML) // 指定邮件内容类型为 HTML.body(String::from("<h2>你好!这是一封由 Rust 通过 126 邮箱发出的测试邮件。</h2>")))?;// 凭证配置//参数是邮箱账号和授权码let creds = Credentials::new(email_address.to_string(), password.to_string());// 创建支持 SMTPS (SSL on port 465) 的邮件客户端//relay自动使用SMTPSlet mailer = SmtpTransport::relay("smtp.126.com")? // 自动使用 SMTPS.port(465).credentials(creds).build();// 发送邮件match mailer.send(&email) {Ok(_) => println!("✅ 邮件发送成功!"),Err(e) => println!("❌ 邮件发送失败: {:?}", e),}Ok(())
}

相关文章:

  • 【FineDance】ModuleNotFoundError: No module named ‘smplx‘
  • Async、await是什么?跟promise有什么区别?使用的好处是什么
  • 常见误区解读之四:相较传统架构,超融合不够稳定?
  • matlab 求fir滤波器系数量化前和量化后的幅频响应对比图
  • 深度解析PECI:平台环境控制接口硬件架构
  • Transformer架构与注意力机制
  • springboot 常用各种注释的含义
  • 深度学习实战文档图像矫正
  • Ubuntu 多网卡安全路由配置(SSH 不断线版)
  • AWS CloudFormation深度解析:构建现代云原生应用基础设施
  • Kafka消费者客户端源码深度解析:从架构到核心流程
  • Java同步机制四大工具对比
  • Java死锁的例子
  • 微信小程序:实现左侧菜单、右侧内容、表单、新增按钮等组件封装
  • 微信小程序传参过来了,但是数据没有获取到
  • 计算机网络学习笔记:TCP可靠传输实现、超时重传时间选择
  • FPGA基础 -- Verilog 禁止语句
  • 电力物联网,5G/4G通讯终端,电力系统通信
  • openstack的实现原理
  • c++读写锁
  • 网站开发可行性报告/seo排名优化公司
  • 如何架设php网站/杭州网站运营十年乐云seo
  • 网站是什么程序做的/拓客软件
  • 没有域名可以做网站/百度会员登录入口
  • 韶关做网站公司/全网投放广告的渠道有哪些
  • wordpress改链接/关键词搜索优化公司