Rust lettre库在Windows 10下无法发送邮件问题求助
Rust脚本Windows下无法发送邮件问题排查与修复
问题背景
开发的Rust脚本用于监控文件夹新文件,上传API后发送邮件。Linux下完全正常,Windows 10中API上传正常,但邮件无法发送。已配置防火墙允许连接,邮件凭证与Linux一致,使用lettre 0.10版本(官方示例Windows可正常运行)。
核心问题分析与修复方案
1. 错误处理不当导致线程崩溃
原代码中send_email_with_attachment函数在发送失败时使用panic!,而notify的监控回调运行在独立线程中,Windows下线程panic不会将错误信息输出到控制台,导致看似无反应,实际线程已崩溃。
修复代码(email_sender.rs):
将panic替换为错误返回:
// 替换原有的match块 match mailer.send(&email) { Ok(_) => { println!("Correo enviado exitosamente!"); Ok(()) }, Err(e) => Err(Box::new(e) as Box<dyn Error>), }
2. TLS配置差异导致SMTP连接失败
lettre 0.10的SmtpTransport::relay在Windows下可能默认TLS配置与Linux不一致,比如Gmail等服务商需要显式启用STARTTLS。建议改用SmtpTransport::builder手动配置TLS:
修改email_sender.rs中的SMTP构建部分:
// 替换原有的SmtpTransport::relay代码 let mailer = SmtpTransport::builder((&smtp_host, 587)) // 明确指定端口,比如Gmail是587 .credentials(creds) .starttls_auto() // 自动启用STARTTLS .build()?;
3. 文件占用问题(Windows特有)
Windows下文件刚创建时可能被写入进程锁定,导致fs::read失败。可增加短暂延迟后重试,或检查文件是否可访问:
在upload_file函数中调用send_email前增加延迟(可选):
// 等待文件写入完成 std::thread::sleep(std::time::Duration::from_millis(500)); // 然后调用send_email_with_attachment
4. 环境变量加载路径问题
Windows下脚本运行的工作目录可能与预期不符,导致.env文件未正确加载。可显式指定.env文件路径:
修改main.rs中的dotenv加载:
// 替换原有的dotenv().ok(); dotenv::from_path(".env").ok(); // 或使用绝对路径,比如Path::new("C:/scripts/.env")
完整修改后的email_sender.rs示例
use std::path::Path; use std::error::Error; use lettre::message::{header, Message}; use lettre::message::{header::ContentType, MultiPart, SinglePart, Attachment}; use lettre::transport::smtp::authentication::Credentials; use lettre::{SmtpTransport, Transport}; use std::{fs, env}; pub fn send_email_with_attachment(file_path: &Path, recipient_email: &str, file_name: &str) -> Result<(), Box<dyn Error>> { let file_name = file_name.to_string(); let file_content = fs::read(file_path)?; // Use the correct ContentType for MiniSEED files let content_type = ContentType::parse("application/vnd.fdsn.mseed").unwrap(); // Create a single part for the attachment let attachment = Attachment::new(file_name.clone()).body(file_content, content_type); let message = String::from("Envio MSeed"); let email = Message::builder() .from("mi_email@gmail.com".parse()?) .to(recipient_email.parse()?) .subject("Envio Miniseed") .multipart( MultiPart::mixed() .singlepart( SinglePart::builder() .header(header::ContentType::TEXT_PLAIN) .body(message), ) .singlepart(attachment), )?; let smtp_username = env::var("SMTP_USERNAME")?; let smtp_password = env::var("SMTP_PASSWORD")?; let smtp_host = env::var("SMTP_HOST")?; let creds = Credentials::new(smtp_username, smtp_password); // Open a remote connection with explicit STARTTLS let mailer = SmtpTransport::builder((&smtp_host, 587)) .credentials(creds) .starttls_auto() .build()?; // Send the email match mailer.send(&email) { Ok(_) => { println!("Correo enviado exitosamente!"); Ok(()) }, Err(e) => Err(Box::new(e)), } }
额外排查建议
- 运行脚本时打开命令提示符(CMD)而非PowerShell,确保错误信息能正常输出
- 测试时直接调用
send_email_with_attachment函数,排除监控回调的线程影响 - 检查SMTP服务器的日志,确认连接请求是否到达服务器
内容的提问来源于stack exchange,提问作者Yafel GH
相关产品推荐
相关产品推荐

