How to Work with Attachments in C++

How to Work with Attachments in C++

Aspose.Email FOSS for C++ provides the mapi_attachment class for creating attachments from raw bytes or streams, and the mapi_message attachment list for reading attachments from existing MSG files. You can add attachments when composing messages and inspect them when loading files, including detecting whether an attachment is itself an embedded MSG message.

Step 1 — Set Up the Project

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)

Step 2 — Add a File Attachment from Bytes

Use mapi_message::add_attachment() to attach binary data when composing a message. Provide the filename, the raw bytes as a std::vector<std::uint8_t>, and the MIME content type:

#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"));
}

Step 3 — Add an Attachment from a File on Disk

Read the file into a byte vector, then pass it to 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"));
}

Step 4 — Read Attachments from an Existing MSG File

message.attachments() returns a const reference to a vector of attachment objects. Each exposes filename, mime_type, data (the raw bytes), and 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';
    }
}

Step 5 — Detect and Access Embedded Messages

Some MSG files contain other MSG files as attachments (forwarded or meeting invitations). Use is_embedded_message() to detect these and access the nested message through 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";
        }
    }
}

Step 6 — Save Attachment Data to Disk

Extract attachment bytes and write them to a file using standard C++ file streams:

#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';
        }
    }
}

Common Issues and Fixes

attachment.data is empty for a known-large attachment. Verify the source MSG file actually contains the attachment data (not an OLE link). MSG files exported from some mail clients may store attachments as external references that the library cannot follow.

is_embedded_message() returns true but embedded_message is nullptr. The attachment header indicates an embedded MSG, but the data was not parseable. Check that the source MSG is valid and not truncated.

Saving extracted attachment fails with a path error. Some attachment filenames contain characters that are invalid on the target file system (e.g., :, ? on Windows). Sanitise attachment.filename before constructing the output path.

Large MSG files cause high memory usage. mapi_message::from_file() loads all attachment data into memory. For large files, open the underlying CFB container with cfb_reader to access individual streams without fully materialising all attachments.

add_attachment() not found at compile time. Ensure you are using the correct include: "aspose/email/foss/msg/mapi_message.hpp". The method is a member of aspose::email::foss::msg::mapi_message.

Frequently Asked Questions

What MIME types should I use for attachments?

Use the standard IANA MIME type for the file format: "text/plain" for .txt, "application/pdf" for .pdf, "application/octet-stream" as a generic fallback for binary files. The MIME type is stored as the attach_mime_tag MAPI property.

Can I attach the same file multiple times?

Yes. Each call to add_attachment() appends an independent attachment entry. Duplicate filenames are permitted; they are distinguished by position in the attachment list.

How do I set the Content-ID for inline images?

Pass the content ID string as the fourth argument to 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 is exposed as attachment.content_id when reading back.

Is attachment.data a copy or a reference?

attachment.data is a std::vector<std::uint8_t> stored by value inside the mapi_message object. Accessing it does not trigger additional I/O.

Can I remove an attachment before saving?

The mapi_message API does not expose a direct remove-attachment method in version 0.1.0. Build a new message, copy the desired properties and attachments manually, and save the new object.

See Also