C++:用 libcurl 发送一封带有附件的邮件

编写mingw C++ 程序,用 libcurl 发送一封带有附件的邮件

下面是一个使用 MinGW 编译的 C++ 程序,使用 libcurl 发送带附件的邮件。这个程序完全通过代码实现 SMTP 邮件发送,不依赖外部邮件客户端:

cpp 复制代码
// send_email.cpp
#include <iostream>
#include <fstream>
#include <curl/curl.h>
#include <cstring>
#include <vector>
using namespace std;

// 用于读取文件内容的回调函数
static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userdata) {
    ifstream *file = static_cast<ifstream*>(userdata);
    if (!file || !file->is_open()) {
        return CURL_READFUNC_ABORT;
    }
    
    file->read(ptr, size * nmemb);
    size_t bytes_read = file->gcount();
    return bytes_read;
}

// 用于生成随机边界字符串
string generate_boundary() {
    static const char alphanum[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    const int len = 24;
    
    string result;
    result.reserve(len);
    
    for (int i = 0; i < len; ++i) {
        result += alphanum[rand() % (sizeof(alphanum) - 1)];
    }
    
    return "----=" + result;
}

int main() {
    CURL *curl;
    CURLcode res = CURLE_OK;
    
    // 邮件配置 - 替换为你的实际信息
    const char *smtp_server = "smtp.gmail.com";
    const char *username = "[email protected]";
    const char *password = "your_app_password";
    const char *from = "[email protected]";
    const char *to = "[email protected]";
    const char *subject = "测试邮件 - 带附件";
    const char *body_text = "这是一封使用 MinGW 和 libcurl 发送的测试邮件,包含附件。";
    const char *attachment_path = "test.txt"; // 附件路径
    
    // 初始化 libcurl
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
    
    if (!curl) {
        cerr << "无法初始化 libcurl" << endl;
        return 1;
    }
    
    // 生成 MIME 边界
    string boundary = generate_boundary();
    
    // 构建完整的邮件内容
    string mime_message;
    
    // 邮件头部
    mime_message = "From: " + string(from) + "\r\n";
    mime_message += "To: " + string(to) + "\r\n";
    mime_message += "Subject: " + string(subject) + "\r\n";
    mime_message += "MIME-Version: 1.0\r\n";
    mime_message += "Content-Type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n";
    
    // 正文部分
    mime_message += "--" + boundary + "\r\n";
    mime_message += "Content-Type: text/plain; charset=utf-8\r\n";
    mime_message += "Content-Transfer-Encoding: 8bit\r\n\r\n";
    mime_message += string(body_text) + "\r\n\r\n";
    
    // 附件部分
    mime_message += "--" + boundary + "\r\n";
    mime_message += "Content-Type: application/octet-stream; name=\"test.txt\"\r\n";
    mime_message += "Content-Disposition: attachment; filename=\"test.txt\"\r\n";
    mime_message += "Content-Transfer-Encoding: base64\r\n\r\n";
    
    // 打开附件文件并编码为 Base64
    ifstream file(attachment_path, ios::binary);
    if (!file.is_open()) {
        cerr << "无法打开附件文件: " << attachment_path << endl;
        return 1;
    }
    
    // 读取文件内容
    vector<char> buffer(
        (istreambuf_iterator<char>(file)),
        istreambuf_iterator<char>()
    );
    file.close();
    
    // Base64 编码
    char *base64_data = curl_easy_escape(curl, buffer.data(), buffer.size());
    if (!base64_data) {
        cerr << "Base64 编码失败" << endl;
        return 1;
    }
    
    mime_message += base64_data;
    curl_free(base64_data);
    
    // 结束 MIME 消息
    mime_message += "\r\n\r\n";
    mime_message += "--" + boundary + "--\r\n";
    
    // 设置 libcurl 选项
    curl_easy_setopt(curl, CURLOPT_URL, string("smtps://" + string(smtp_server)).c_str());
    curl_easy_setopt(curl, CURLOPT_USERNAME, username);
    curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from);
    
    struct curl_slist *recipients = NULL;
    recipients = curl_slist_append(recipients, to);
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
    
    curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 仅用于测试,生产环境应设为1L
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 仅用于测试,生产环境应设为2L
    
    // 设置读取回调
    istringstream mime_stream(mime_message);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    curl_easy_setopt(curl, CURLOPT_READDATA, &mime_stream);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
    // 设置邮件大小
    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)mime_message.size());
    
    // 发送邮件
    res = curl_easy_perform(curl);
    
    // 检查结果
    if (res != CURLE_OK) {
        cerr << "邮件发送失败: " << curl_easy_strerror(res) << endl;
    } else {
        cout << "邮件发送成功!" << endl;
    }
    
    // 清理资源
    curl_slist_free_all(recipients);
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    
    return (int)res;
}

编译和运行说明

  1. 安装依赖

  2. 编译命令

    bash 复制代码
    g++ -o send_email.exe send_email.cpp -lcurl -lssl -lcrypto -lz -lws2_32
  3. 配置邮箱

  4. 替换以下信息

    • [email protected]:你的邮箱地址
    • your_app_password:邮箱密码或应用专用密码
    • [email protected]:收件人邮箱
    • test.txt:附件文件路径(确保文件存在)

关键功能说明

  1. MIME 消息构建

    • 程序构建了符合 MIME 标准的邮件结构
    • 包含文本正文和附件部分
    • 使用随机生成的边界分隔符
  2. Base64 编码

    • 附件内容使用 Base64 编码
    • 使用 curl_easy_escape 进行编码
  3. SMTP 设置

    • 使用 SMTPS (SSL/TLS)
    • 设置发件人、收件人列表
    • 提供用户名和密码进行认证
  4. 错误处理

    • 检查文件是否成功打开
    • 检查 Base64 编码是否成功
    • 检查 curl 操作返回值

注意事项

  1. 安全性

    • 示例中禁用了 SSL 证书验证(仅用于测试)

    • 生产环境中应启用验证并提供 CA 证书

      cpp 复制代码
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
      curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
      curl_easy_setopt(curl, CURLOPT_CAINFO, "path/to/cacert.pem");
  2. 大文件支持

    • 当前实现将整个文件读入内存
    • 对于大文件,应使用流式处理
  3. 编码问题

    • 确保文本内容使用 UTF-8 编码
    • 文件名如有中文可能需要特殊处理
  4. 调试

    cpp 复制代码
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 启用详细输出

这个程序可以直接在 MinGW 环境下编译运行,实现了完整的 SMTP 邮件发送功能,包括文本正文和文件附件。


编写 VC++ 程序,用 libcurl 发送一封带有附件的邮件

下面是一个使用 libcurl 库在 VC++ 中发送带附件邮件的完整示例。这个程序直接通过 SMTP 协议发送邮件,不依赖邮件客户端:

cpp 复制代码
#include <iostream>
#include <string>
#include <curl/curl.h>

// 用于存储邮件内容的回调函数
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    FILE *readhere = (FILE *)userp;
    return fread(ptr, size, nmemb, readhere);
}

int main()
{
    CURL *curl;
    CURLcode res = CURLE_OK;
    struct curl_slist *recipients = NULL;
    FILE *hd_src = NULL;
    struct curl_slist *headers = NULL;
    const char *file_path = "C:\\test.txt"; // 替换为你的附件路径

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl) {
        // 设置SMTP服务器和端口 (这里以Gmail为例)
        curl_easy_setopt(curl, CURLOPT_URL, "smtps://smtp.gmail.com:465");
        
        // 设置登录凭据 (替换为你的邮箱和密码/应用密码)
        curl_easy_setopt(curl, CURLOPT_USERNAME, "[email protected]");
        curl_easy_setopt(curl, CURLOPT_PASSWORD, "your_app_password");
        
        // 启用SSL/TLS
        curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
        
        // 设置发件人
        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "[email protected]");
        
        // 添加收件人
        recipients = curl_slist_append(recipients, "[email protected]");
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
        
        // 创建邮件头
        headers = curl_slist_append(headers, "Subject: 带附件的测试邮件");
        headers = curl_slist_append(headers, "To: [email protected]");
        headers = curl_slist_append(headers, "From: [email protected]");
        headers = curl_slist_append(headers, "Mime-Version: 1.0");
        headers = curl_slist_append(headers, "Content-Type: multipart/mixed; boundary=\"boundary-string\"");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        
        // 构建邮件正文和附件
        std::string body = 
            "--boundary-string\r\n"
            "Content-Type: text/plain; charset=utf-8\r\n"
            "Content-Transfer-Encoding: 7bit\r\n\r\n"
            "这是一封使用libcurl发送的测试邮件。\r\n"
            "包含一个文本文件附件。\r\n\r\n"
            "--boundary-string\r\n"
            "Content-Type: text/plain; name=\"test.txt\"\r\n"
            "Content-Disposition: attachment; filename=\"test.txt\"\r\n"
            "Content-Transfer-Encoding: base64\r\n\r\n";
        
        // 打开附件文件
        hd_src = fopen(file_path, "rb");
        if (!hd_src) {
            std::cerr << "无法打开附件文件: " << file_path << std::endl;
            return 1;
        }
        
        // 创建回调数据源
        curl_mime *mime = curl_mime_init(curl);
        curl_mimepart *part = curl_mime_addpart(mime);
        
        // 设置邮件正文
        curl_mime_data(part, body.c_str(), body.size());
        
        // 添加附件
        part = curl_mime_addpart(mime);
        curl_mime_filedata(part, file_path);
        curl_mime_name(part, "attachment");
        curl_mime_filename(part, "test.txt");
        curl_mime_type(part, "text/plain");
        
        // 设置多部分结束边界
        part = curl_mime_addpart(mime);
        curl_mime_data(part, "\r\n--boundary-string--\r\n", CURL_ZERO_TERMINATED);
        
        curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
        
        // 发送邮件
        res = curl_easy_perform(curl);
        
        // 检查错误
        if (res != CURLE_OK)
            std::cerr << "发送失败: " << curl_easy_strerror(res) << std::endl;
        else
            std::cout << "邮件发送成功!" << std::endl;
        
        // 清理资源
        if (hd_src) fclose(hd_src);
        curl_slist_free_all(recipients);
        curl_slist_free_all(headers);
        curl_mime_free(mime);
        curl_easy_cleanup(curl);
    }
    
    curl_global_cleanup();
    return (int)res;
}

关键步骤说明:

  1. 初始化libcurl

    cpp 复制代码
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();
  2. 配置SMTP服务器

    cpp 复制代码
    curl_easy_setopt(curl, CURLOPT_URL, "smtps://smtp.gmail.com:465");
  3. 设置认证信息

    cpp 复制代码
    curl_easy_setopt(curl, CURLOPT_USERNAME, "[email protected]");
    curl_easy_setopt(curl, CURLOPT_PASSWORD, "your_app_password");
  4. 添加收件人

    cpp 复制代码
    recipients = curl_slist_append(recipients, "[email protected]");
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
  5. 构建邮件头

    cpp 复制代码
    headers = curl_slist_append(headers, "Subject: 带附件的测试邮件");
    // ...其他头部信息...
  6. 添加附件

    cpp 复制代码
    curl_mime *mime = curl_mime_init(curl);
    curl_mimepart *part = curl_mime_addpart(mime);
    curl_mime_filedata(part, file_path);

使用前的准备工作:

  1. 安装libcurl

    • 下载libcurl:https://curl.se/windows/
    • 在VC++项目中配置:
      • 包含目录:添加curl头文件路径
      • 库目录:添加lib文件路径
      • 链接器输入:添加libcurl.lib
  2. 邮箱设置

  3. 替换以下信息

常见问题解决:

  1. 认证失败

    • 确保邮箱启用了SMTP访问
    • 检查用户名密码是否正确
    • 尝试使用应用专用密码
  2. 附件无法打开

    • 确认文件路径正确
    • 检查文件访问权限
    • 使用绝对路径
  3. SSL连接问题

  4. 调试信息

    cpp 复制代码
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 启用详细输出

这个程序可以直接集成到VC++项目中,实现无需用户交互的邮件发送功能。

相关推荐
Bardb5 分钟前
01__C++入门
c++·qt
weixin_457665391 小时前
C++11新标准
开发语言·c++
奔跑吧邓邓子2 小时前
解锁Vscode:C/C++环境配置超详细指南
c语言·c++·vscode·配置指南
虾球xz2 小时前
CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx
开发语言·c++·物联网·学习
liujing102329293 小时前
Day09_刷题niuke20250609
java·c++·算法
Bardb3 小时前
02__C++的基本语法
c++·qt
freyazzr4 小时前
C++八股 | Day3 | 智能指针 / 内存管理 / 内存分区 / 内存对齐
开发语言·c++
闻缺陷则喜何志丹4 小时前
【动态规划】B4336 [中山市赛 2023] 永别|普及+
c++·算法·动态规划·洛谷
序属秋秋秋4 小时前
《C++初阶之入门基础》【普通引用 + 常量引用 + 内联函数 + nullptr】
开发语言·c++·笔记
筏.k4 小时前
C++ 网络编程(10) asio处理粘包的简易方式
java·网络·c++