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_message → save() 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()andsave_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
| Issue | Cause | Fix |
|---|---|---|
load_from_eml throws on a valid file | Non-CRLF line endings in EML | Ensure EML uses RFC 5322 CRLF; some export tools write bare LF |
| Missing attachments after EML → MSG | Non-standard MIME attachment encoding | Verify EML uses multipart/mixed with standard Content-Disposition: attachment |
| Empty HTML body after MSG → EML | Source MSG has only plain-text body | Check message.html_body(); if empty, only plain text was stored |
| Stream output is corrupt | Stream opened in text mode | Always use std::ios::binary |
Subject shows ?= sequences | RFC 2047 encoded-word in original EML | Decode 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 / Method | Description |
|---|---|
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> |