Rust Lettre 库无法在 Windows 上发送电子邮件

问题描述 投票:0回答:1

我正在开发一个 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 上运行,但我使用该库创建了自己的代码,但此时它不发送。

图片: https://imgur.com/a/9fDCgSA

linux windows rust rust-cargo
1个回答
0
投票

lettre 的默认功能集包括

native-tls
功能,使您能够使用
SmtpTransport::relay
构造函数。

我检查了文档,它说

native-tls
功能在 Windows 上使用
schannel
。可能是您的脚本工作所需的依赖项

© www.soinside.com 2019 - 2024. All rights reserved.