You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Rust的google_gmail1库结合服务账户发送带附件邮件

解决Rust使用google_gmail1发送带附件邮件的问题

核心结论

发送带附件的邮件不需要使用upload_resumable()方法,该方法适用于上传大附件到草稿等场景。正确的方式是构建完整的MIME格式邮件,编码为base64url后赋值给Message的raw字段,再调用messages_send接口发送。

具体实现步骤

1. 添加必要依赖

在Cargo.toml中添加构建MIME消息和编码所需的依赖:

[dependencies]
google-gmail1 = "7.0.1"
mime = "0.3.17"
base64 = "0.21.5"
tokio = { version = "1.0", features = ["full"] }

2. 构建MIME格式邮件

编写工具函数生成包含正文、附件的完整MIME邮件:

use mime::Mime;
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
use base64::Engine as _;
use std::io::Write;

fn build_mime_email(
    to: &str,
    subject: &str,
    content: &str,
    file_name: &str,
    file_data: &[u8],
    file_mime: &Mime
) -> Vec<u8> {
    let boundary = "----RustGmailAttachmentBoundary";
    let mut mime = Vec::new();

    // 邮件头部
    writeln!(&mut mime, "To: {}", to).unwrap();
    writeln!(&mut mime, "Subject: {}", subject).unwrap();
    writeln!(&mut mime, "Content-Type: multipart/mixed; boundary=\"{}\"", boundary).unwrap();
    writeln!(&mut mime).unwrap(); // 头部与内容之间必须有空行

    // 文本正文部分
    writeln!(&mut mime, "--{}", boundary).unwrap();
    writeln!(&mut mime, "Content-Type: text/plain; charset=utf-8").unwrap();
    writeln!(&mut mime).unwrap();
    writeln!(&mut mime, "{}", content).unwrap();

    // 附件部分
    writeln!(&mut mime, "\n--{}", boundary).unwrap();
    writeln!(&mut mime, "Content-Type: {}", file_mime).unwrap();
    writeln!(&mut mime, "Content-Disposition: attachment; filename=\"{}\"", file_name).unwrap();
    writeln!(&mut mime, "Content-Transfer-Encoding: base64").unwrap();
    writeln!(&mut mime).unwrap();
    // 附件内容按MIME规范分割为每行76字符的base64编码
    let encoded_file = STANDARD.encode(file_data);
    for chunk in encoded_file.as_bytes().chunks(76) {
        writeln!(&mut mime, "{}", String::from_utf8_lossy(chunk)).unwrap();
    }

    // 结束MIME边界
    writeln!(&mut mime, "\n--{}--", boundary).unwrap();

    mime
}

3. 构建请求并发送邮件

结合你已有的代码,完成邮件发送逻辑:

use google_gmail1::{api::Message, hyper, hyper_rustls, oauth2, Gmail};
use std::fs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 读取服务账户密钥
    let secret = oauth2::read_service_account_key("service_account.json")?;
    // 创建Gmail客户端(需配置域范围委派,模拟目标用户)
    let auth = oauth2::ServiceAccountAuthenticator::builder(secret)
        .with_scopes(&["https://www.googleapis.com/auth/gmail.send"])
        .with_subject("target-user@your-domain.com") // 替换为要模拟的用户邮箱
        .build()
        .await?;
    let hub = Gmail::new(hyper::Client::builder().build(hyper_rustls::HttpsConnectorBuilder::new().with_native_roots().https_or_http().enable_http1().build()), auth);

    // 你的现有参数
    let to = "recipient@example.com".to_string();
    let subject = "带附件的测试邮件".to_string();
    let content = "这是邮件正文内容".to_string();
    let file_name = "test.csv".to_string();
    let file_data = fs::read(&file_name)?;
    let file_mime: Mime = "text/csv".parse()?; // 根据实际文件类型调整MIME类型

    // 构建MIME邮件并编码为base64url
    let mime_email = build_mime_email(&to, &subject, &content, &file_name, &file_data, &file_mime);
    let raw = URL_SAFE_NO_PAD.encode(&mime_email);

    // 创建Message请求
    let req = Message {
        raw: Some(raw),
        ..Message::default()
    };

    // 发送邮件("me"代表当前认证的用户,即模拟的target-user)
    let (_, message) = hub.users().messages_send(req, "me").doit().await?;
    println!("邮件发送成功,ID: {:?}", message.id);

    Ok(())
}

关键说明

  1. 附件存放位置:附件内容需和邮件正文、头部一起封装成完整MIME邮件,无需通过upload_resumable()传递。upload_resumable()仅用于上传大附件到草稿等特殊场景,不适合直接发送完整邮件。
  2. 服务账户配置:确保服务账户已开启域范围委派,并授予Gmail Send权限,同时在代码中指定要模拟的用户邮箱(with_subject)——服务账户本身没有Gmail邮箱,必须模拟域内用户发送。
  3. MIME类型:附件的MIME类型需与文件实际类型匹配(如text/csv对应CSV文件,application/pdf对应PDF文件),避免解析错误。

内容的提问来源于stack exchange,提问作者啊鹿Dizzyi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:20:40