如何在 C++ 中处理附件

如何在 C++ 中处理附件

Aspose.Email FOSS for C++ 提供了 mapi_attachment 类,用于从原始字节或流创建附件,并提供 mapi_message 附件列表,用于从现有的 MSG 文件中读取附件。您可以在撰写消息时添加附件,并在加载文件时检查它们,包括检测附件是否本身是嵌入的 MSG 消息。

步骤 1 — 设置项目

git clone https://github.com/aspose-email-foss/Aspose.Email-FOSS-for-Cpp.git
add_subdirectory(Aspose.Email-FOSS-for-Cpp)
target_link_libraries(your_target PRIVATE AsposeEmailFoss::AsposeEmailFoss)

步骤 2 — 从字节添加文件附件

在撰写消息时使用 mapi_message::add_attachment() 附加二进制数据。提供文件名、作为 std::vector<std::uint8_t> 的原始字节,以及 MIME 内容类型:

#include <filesystem>
#include <vector>
#include "aspose/email/foss/msg/mapi_message.hpp"

int main()
{
    auto message = aspose::email::foss::msg::mapi_message::create(
        "Report", "Please review the attached report.");
    message.set_sender_name("Alice");
    message.set_sender_email_address("alice@example.com");
    message.add_recipient("bob@example.com", "Bob");

    // Attach a text file
    std::vector<std::uint8_t> text_data{'H', 'e', 'l', 'l', 'o'};
    message.add_attachment("notes.txt", text_data, "text/plain");

    // Attach a binary blob
    std::vector<std::uint8_t> bin_data{0x00, 0x01, 0x02, 0x03};
    message.add_attachment("data.bin", bin_data, "application/octet-stream");

    message.save(std::filesystem::path("report.msg"));
}

步骤 3 — 从磁盘文件添加附件

将文件读取到字节向量中,然后将其传递给 add_attachment():

#include <filesystem>
#include <fstream>
#include <iterator>
#include <vector>
#include "aspose/email/foss/msg/mapi_message.hpp"

int main()
{
    // Read a file into bytes
    std::ifstream file("document.pdf", std::ios::binary);
    std::vector<std::uint8_t> pdf_bytes(
        std::istreambuf_iterator<char>(file),
        std::istreambuf_iterator<char>{});

    auto message = aspose::email::foss::msg::mapi_message::create(
        "Document", "See attached PDF.");
    message.set_sender_name("Alice");
    message.set_sender_email_address("alice@example.com");
    message.add_recipient("bob@example.com", "Bob");
    message.add_attachment("document.pdf", pdf_bytes, "application/pdf");

    message.save(std::filesystem::path("with_pdf.msg"));
}

步骤 4 — 从现有的 MSG 文件中读取附件

message.attachments() 返回一个 const 引用,指向附件对象的向量。每个对象都公开 filename、mime_type、data(原始字节),以及 content_id:

#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"

int main()
{
    std::ifstream input("sample.msg", std::ios::binary);
    const auto message = aspose::email::foss::msg::mapi_message::from_stream(input);

    std::cout << "Attachments: " << message.attachments().size() << '\n';
    for (std::size_t i = 0; i < message.attachments().size(); ++i)
    {
        const auto& a = message.attachments()[i];
        std::cout << "[" << (i + 1) << "]"
                  << "  name="         << a.filename
                  << "  mime="         << a.mime_type
                  << "  bytes="        << a.data.size()
                  << "  content_id="   << a.content_id
                  << '\n';
    }
}

步骤 5 — 检测并访问嵌入的消息

某些 MSG 文件包含其他 MSG 文件作为附件(转发的或会议邀请)。使用 is_embedded_message() 检测这些文件,并通过 embedded_message 访问嵌套的消息:

#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"

int main()
{
    std::ifstream input("sample.msg", std::ios::binary);
    const auto message = aspose::email::foss::msg::mapi_message::from_stream(input);

    for (const auto& attachment : message.attachments())
    {
        if (attachment.is_embedded_message() && attachment.embedded_message != nullptr)
        {
            const auto& inner = *attachment.embedded_message;
            std::cout << "Embedded subject: " << inner.subject() << '\n';
            std::cout << "Embedded from:    " << inner.sender_email_address() << '\n';

            // Save the embedded message as a standalone MSG
            inner.save(std::filesystem::path("embedded.msg"));
        }
        else
        {
            std::cout << "Regular attachment: " << attachment.filename
                      << " (" << attachment.data.size() << " bytes)\n";
        }
    }
}

步骤 6 — 将附件数据保存到磁盘

提取附件字节并使用标准 C++ 文件流将其写入文件:

#include <filesystem>
#include <fstream>
#include <iostream>
#include "aspose/email/foss/msg/mapi_message.hpp"

int main()
{
    const auto message = aspose::email::foss::msg::mapi_message::from_file(
        std::filesystem::path("sample.msg"));

    const std::filesystem::path out_dir("attachments");
    std::filesystem::create_directories(out_dir);

    for (const auto& a : message.attachments())
    {
        if (!a.is_embedded_message())
        {
            const auto out_path = out_dir / a.filename;
            std::ofstream out(out_path, std::ios::binary);
            out.write(reinterpret_cast<const char*>(a.data.data()),
                      static_cast<std::streamsize>(a.data.size()));
            std::cout << "Saved: " << out_path << '\n';
        }
    }
}

常见问题与解决方案

attachment.data 对于已知的大附件,它为空。 请验证源 MSG 文件实际上包含附件数据(而不是 OLE 链接)。MSG 文件从某些邮件客户端导出时可能将附件存储为外部引用,库无法跟踪。

is_embedded_message() returns true but embedded_message is nullptr. 附件头部指示嵌入了 MSG,但数据无法解析。请检查源 MSG 是否有效且未被截断。

保存提取的附件时出现路径错误,导致失败。 某些附件文件名包含在目标文件系统上无效的字符(例如, :, ? 在 Windows 上)。请清理 attachment.filename 在构建输出路径之前。

大型 MSG 文件会导致高内存使用。 mapi_message::from_file() 将所有附件数据加载到内存中。对于大文件,请使用底层 CFB 容器的方式打开 cfb_reader 以在不完整加载所有附件的情况下访问各个流。

add_attachment() 在编译时未找到。 确保您使用了正确的 include: "aspose/email/foss/msg/mapi_message.hpp"。该方法是成员的 aspose::email::foss::msg::mapi_message.

常见问题

我应该为附件使用哪种 MIME 类型?

使用标准的 IANA MIME 类型来表示文件格式:"text/plain" 用于 .txt,"application/pdf" 用于 .pdf,"application/octet-stream" 作为二进制文件的通用后备。MIME 类型存储为 attach_mime_tag MAPI 属性。

我可以多次附加同一个文件吗?

是的。每次调用 add_attachment() 都会追加一个独立的附件条目。允许重复的文件名;它们通过在附件列表中的位置来区分。

如何为内联图像设置 Content-ID?

将内容 ID 字符串作为第四个参数传递给 mapi_attachment::from_stream():

auto att = aspose::email::foss::msg::mapi_attachment::from_bytes(
    "logo.png", png_bytes, "image/png", "<logo-cid@example.com>");

读取时,content_id 以 attachment.content_id 的形式暴露。

attachment.data 是副本还是引用?

attachment.data 是存储在 mapi_message 对象内部的按值存储的 std::vector<std::uint8_t>。访问它不会触发额外的 I/O。

我可以在保存之前删除附件吗?

mapi_message API 在版本 0.1.0 中未提供直接的 remove-attachment 方法。构建一个新消息,手动复制所需的属性和附件,然后保存新对象。

另请参阅

 中文