我正在开发一个 Rust 脚本,用于监视文件夹中的新文件,当它检测到新文件时,会将它们发送到 api,然后通过邮件发送。到目前为止,一切都很好,我已经在 Linux 中测试和编译了它,它运行得很好,但是当我在 Windows 10 中运行它时,它不会发送电子邮件,但如果它上传到 api 没有问题,应该注意的是,我我已经从 Windows 创建了一个项目,并且放置了相同的代码。在 Windows 中,我已经将防火墙配置为允许连接,但它仍然不发送邮件,凭据和一切都是一样的。
main.rs
use std::fs;
use std::path::Path;
use std::collections::HashSet;
use notify::{Watcher, RecommendedWatcher, RecursiveMode, Result, EventKind, Event};
use reqwest;
mod email_sender;
// Importa el módulo email_senders.rs
use dotenv::dotenv;
// Importa la biblioteca dotenv
use std::env;
fn load_sent_files() -> HashSet<String> {
if let Ok(content) = fs::read_to_string("sent_files.txt") {
content.lines().map(|line| line.to_string()).collect()
} else {
HashSet::new()
}
}
fn save_sent_files(sent_files: &HashSet<String>) {
let content: Vec<String> = sent_files.iter().cloned().collect();
if let Err(e) = fs::write("sent_files.txt", content.join("\n")) {
println!("Error al guardar los archivos enviados: {:?}", e);
}
}
fn upload_file(file_path: &Path, sent_files: &mut HashSet<String>) {
let file_name = file_path.file_name().unwrap().to_str().unwrap().to_string();
if sent_files.contains(&file_name) {
println!("Archivo {} ya ha sido enviado anteriormente.", file_name);
return;
}
let client = reqwest::blocking::Client::new();
let url = "http://localhost:8080/upload"; // Ajusta esto a la URL de tu API
let form = reqwest::blocking::multipart::Form::new()
.file("file", file_path)
.unwrap(); // Puedes manejar este error de una mejor manera si lo deseas
let response = client.post(url)
.multipart(form)
.send();
match response {
Ok(resp) => {
if resp.status().is_success() {
// Obtiene el cuerpo de la respuesta y lo muestra.
let response_body = resp.text().unwrap_or_else(|_| String::from("Respuesta vacía"));
println!("Respuesta del servidor: {}", response_body);
println!("Archivo enviado con éxito: {}", file_name);
// Agrega el nombre del archivo al conjunto de archivos enviados
sent_files.insert(file_name.clone());
save_sent_files(&sent_files); // Guarda el conjunto actualizado en el archivo
// Envía el archivo adjunto por correo electrónico
let recipient_email = env::var("RECIPIENT_EMAIL").expect("RECIPIENT_EMAIL not set");
if let Err(e) = email_sender::send_email_with_attachment(file_path, &recipient_email,&file_name) {
println!("Error al enviar el correo electrónico: {:?}", e);
}
} else {
println!("Error al enviar el archivo: {:?}", resp.text().unwrap_or_else(|_| String::from("Error desconocido")));
}
}
Err(e) => {
println!("Error en la solicitud: {:?}", e);
}
}
}
fn main() -> Result<()> {
println!("Monitor de carpeta iniciado.");
// Carga las variables de entorno desde el archivo .env
dotenv().ok();
let mut sent_files = load_sent_files(); // Carga el conjunto de archivos enviados al iniciar
let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res: Result<Event>| {
match res {
Ok(event) => {
if let EventKind::Create(_) = event.kind {
let file_path = &event.paths[0];
println!("Archivo nuevo detectado: {:?}", file_path);
upload_file(file_path, &mut sent_files); // Pasa el conjunto a la función upload_file
}
}
Err(e) => println!("Error al monitorear: {:?}", e),
}
})?;
let folder_path = env::var("FOLDER_PATH").expect("FOLDER_PATH not set");
watcher.watch(Path::new(&folder_path), RecursiveMode::Recursive)?;
std::thread::park();
Ok(())
}
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 anyhow::Result;
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("[email protected]".parse().unwrap())
.to(recipient_email.parse().unwrap())
.subject("Envio Miniseed")
.multipart(
MultiPart::mixed()
.singlepart(
SinglePart::builder()
.header(header::ContentType::TEXT_PLAIN)
.body(message),
)
.singlepart(attachment),
)
.unwrap();
let smtp_username = env::var("SMTP_USERNAME").expect("SMTP_USERNAME not set");
let smtp_password = env::var("SMTP_PASSWORD").expect("SMTP_PASSWORD not set");
let smtp_host = env::var("SMTP_HOST").expect("SMTP_HOST not set");
let creds = Credentials::new(smtp_username, smtp_password);
// Open a remote connection to gmail
let mailer = SmtpTransport::relay(&smtp_host)?
.credentials(creds)
.build();
// Send the email
match mailer.send(&email) {
Ok(_) => Ok(println!("Correo enviado exitosamente!")),
Err(e) => panic!("Error al enviar correo: {:?}", e),
}
}
货物.toml
[dependencies]
notify = { version = "6.0.1", features = ["serde"] }
reqwest = { version = "0.11", features = ["json", "blocking", "multipart"] }
tokio = { version = "1", features = ["full"] }
log = "0.4.19"
lettre = "0.10"
mime = "0.3.17"
dotenv = "0.15.0"
anyhow = "1.0.57"
在 Windows 中,我已经将防火墙配置为允许连接,但它仍然不发送邮件,凭据和一切都相同。 有一个版本为 0.10 的库的示例,它可以在 Windows 上运行,但我使用该库创建了自己的代码,但此时它不发送。
lettre 的默认功能集包括
native-tls
功能,使您能够使用 SmtpTransport::relay
构造函数。
我检查了文档,它说
native-tls
功能在 Windows 上使用 schannel
。可能是您的脚本工作所需的依赖项