EML and MSG Conversion

EML and MSG Conversion

Aspose.Email FOSS for C++ converts bidirectionally between EML (RFC 5322 / MIME) and MSG (Outlook MAPI) formats using mapi_message. The conversion path is: load_from_eml() → in-memory mapi_messagesave() for EML→MSG, and from_file() / from_stream()save_to_eml() for MSG→EML.


Convert EML to MSG

Load an EML file with mapi_message::load_from_eml(), then serialize to MSG with save():

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

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

    std::cout << "Subject: " << message.subject() << '\n';
    message.save(std::filesystem::path("converted.msg"));
    std::cout << "Saved converted.msg\n";
}

load_from_eml() parses the MIME structure and populates the MAPI subject, plain-text body, HTML body, sender identity, recipient list, and all attachment parts.


Convert MSG to EML

Load an MSG file with mapi_message::from_file(), then call save_to_eml():

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

    message.save_to_eml(std::filesystem::path("exported.eml"));
    std::cout << "Saved exported.eml\n";
}

Stream-Based Conversion

All four relevant methods accept std::istream / std::ostream overloads for pipeline-style processing without touching the filesystem:

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

int main()
{
    // EML → MSG via streams
    std::ifstream eml_in("message.eml", std::ios::binary);
    const auto message = aspose::email::foss::msg::mapi_message::load_from_eml(eml_in);

    std::ofstream msg_out("converted.msg", std::ios::binary);
    message.save(msg_out);

    // MSG → EML via streams
    std::ifstream msg_in("converted.msg", std::ios::binary);
    const auto reloaded = aspose::email::foss::msg::mapi_message::from_stream(msg_in);

    std::ofstream eml_out("roundtrip.eml", std::ios::binary);
    reloaded.save_to_eml(eml_out);
}

Always open streams with std::ios::binary. Text-mode streams translate line endings on Windows, corrupting binary CFB data.


In-Memory Conversion (Bytes)

save() and save_to_eml() both have zero-argument overloads returning std::vector<std::uint8_t>. Use these to avoid all filesystem I/O:

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

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

    const std::vector<std::uint8_t> msg_bytes  = message.save();
    const std::vector<std::uint8_t> eml_bytes  = message.save_to_eml();

    // Write to a database, network socket, or other destination...
}

Batch Directory Conversion

Iterate a directory with std::filesystem::directory_iterator to convert all .eml files in one pass:

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

int main()
{
    const std::filesystem::path input_dir("emails/eml");
    const std::filesystem::path output_dir("emails/msg");
    std::filesystem::create_directories(output_dir);

    for (const auto& entry : std::filesystem::directory_iterator(input_dir))
    {
        if (entry.path().extension() != ".eml")
            continue;

        const auto message = aspose::email::foss::msg::mapi_message::load_from_eml(
            entry.path());

        auto out_path = output_dir / entry.path().filename();
        out_path.replace_extension(".msg");
        message.save(out_path);

        std::cout << "Converted: " << entry.path().filename() << '\n';
    }
}

Tips and Best Practices

  • Open all file streams with std::ios::binary — both input and output.
  • Prefer load_from_eml(path) over the stream overload when you have a path; the path overload handles file opening internally and is slightly simpler.
  • Use the bytes overloads (save() and save_to_eml() with no args) when piping data between services to avoid temporary files.
  • Subject, body, HTML body, sender, recipients, and MIME attachments are all preserved through the full round-trip.
  • IMAP flags, routing headers, and TNEF-encoded content are not carried through conversion.

Common Issues

IssueCauseFix
load_from_eml throws on a valid fileNon-CRLF line endings in EMLEnsure EML uses RFC 5322 CRLF; some export tools write bare LF
Missing attachments after EML → MSGNon-standard MIME attachment encodingVerify EML uses multipart/mixed with standard Content-Disposition: attachment
Empty HTML body after MSG → EMLSource MSG has only plain-text bodyCheck message.html_body(); if empty, only plain text was stored
Stream output is corruptStream opened in text modeAlways use std::ios::binary
Subject shows ?= sequencesRFC 2047 encoded-word in original EMLDecode with an RFC 2047 library; the library preserves the encoded form

FAQ

Does EML → MSG preserve the HTML body?

Yes. A text/html MIME part is stored in the MAPI HTML body property and is accessible via message.html_body() after loading.

Are attachments preserved in both directions?

Yes. All MIME attachment parts are carried through the mapi_message attachment list. Binary attachment data is available as attachment.data (a std::vector<std::uint8_t>).

Can I convert a single EML to multiple output formats simultaneously?

Yes. Load the EML once with load_from_eml(), then call both save() and save_to_eml() on the same in-memory mapi_message object.

Is load_from_eml() thread-safe?

Each call creates an independent mapi_message instance. Multiple threads may call load_from_eml() concurrently as long as each thread uses its own output object.

What happens to messages with no plain-text body?

If the EML contains only a text/html part, message.body() returns an empty string and message.html_body() carries the HTML content. Both are preserved through MSG serialization.


API Reference Summary

Class / MethodDescription
mapi_message::load_from_eml(path)Parse an EML file into a mapi_message
mapi_message::load_from_eml(stream)Parse EML from an std::istream
mapi_message::save(path)Serialize mapi_message to an MSG file
mapi_message::save(stream)Write MSG bytes to an std::ostream
mapi_message::save()Return MSG as std::vector<std::uint8_t>
mapi_message::from_file(path)Load an MSG file into a mapi_message
mapi_message::from_stream(stream)Load MSG from an std::istream
mapi_message::save_to_eml(path)Write EML to a file path
mapi_message::save_to_eml(stream)Write EML bytes to an std::ostream
mapi_message::save_to_eml()Return EML as std::vector<std::uint8_t>