读取 MSG 文件
读取 MSG 文件
mapi_message 是加载和解析 Outlook MSG 文件的入口点。一次调用 from_file() 或 from_stream() 即可读取完整的 CFB 容器,提取所有 MAPI 属性,并通过类型化的访问器方法公开——无需安装 Outlook。
从文件路径加载
使用 mapi_message::from_file() 与 std::filesystem::path:
#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"));
std::cout << "Subject: " << message.subject() << '\n';
std::cout << "From: " << message.sender_name()
<< " <" << message.sender_email_address() << ">\n";
std::cout << "Body: " << message.body() << '\n';
}从流加载
当 MSG 数据来自网络套接字、归档文件或任何 std::istream 时,使用 mapi_message::from_stream():
#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 << "Subject: " << message.subject() << '\n';
}始终使用 std::ios::binary 打开流。文本模式的流在 Windows 上会转换换行符,从而损坏 CFB 容器。
访问消息属性
mapi_message 将最常用的消息属性公开为具名 getter:
#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"));
// Core properties
std::cout << "Subject: " << message.subject() << '\n';
std::cout << "Sender name: " << message.sender_name() << '\n';
std::cout << "Sender email: " << message.sender_email_address() << '\n';
std::cout << "Internet msg-id: " << message.internet_message_id() << '\n';
std::cout << "Message class: " << message.message_class() << '\n';
// Body — check html_body() first; fall back to plain body()
const auto& body = message.html_body().empty()
? message.body()
: message.html_body();
std::cout << "Body length: " << body.size() << " chars\n";
}遍历收件人
message.recipients() 返回一个 std::vector,其中包含收件人对象,具备 display_name、email_address 和 recipient_type:
#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"));
for (std::size_t i = 0; i < message.recipients().size(); ++i)
{
const auto& r = message.recipients()[i];
std::cout << "[" << (i + 1) << "] "
<< r.display_name << " <" << r.email_address << ">"
<< " type=" << r.recipient_type << '\n';
}
}遍历附件
message.attachments() 返回一个 std::vector,其中包含附件对象。每个对象公开 filename、mime_type、data、content_id 和 is_embedded_message():
#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"));
for (const auto& a : message.attachments())
{
std::cout << a.filename
<< " " << a.mime_type
<< " " << a.data.size() << " bytes"
<< " embedded=" << (a.is_embedded_message() ? "yes" : "no")
<< '\n';
if (!a.is_embedded_message())
{
std::ofstream out("extracted_" + a.filename, std::ios::binary);
out.write(reinterpret_cast<const char*>(a.data.data()),
static_cast<std::streamsize>(a.data.size()));
}
}
}访问嵌入式消息
当 is_embedded_message() 为 true 时,附件包含另一个 MSG 文件。通过 attachment.embedded_message 访问它:
#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"));
for (const auto& a : message.attachments())
{
if (a.is_embedded_message() && a.embedded_message != nullptr)
{
const auto& inner = *a.embedded_message;
std::cout << "Embedded subject: " << inner.subject() << '\n';
inner.save(std::filesystem::path("embedded.msg"));
}
}
}提示与最佳实践
- 加载 MSG 文件时,始终将
std::ios::binary传递给std::ifstream。 - 在
body()之前检查html_body()—— HTML 专用的消息将其内容存储在 HTML body 属性中,并将body()留空。 from_file()将所有附件数据加载到内存中。对于大型 MSG 文件,考虑使用msg_reader打开底层容器,以在不完全实例化附件字节的情况下检查元数据。from_file(path, strict)和from_stream(stream, strict)的strict参数默认值为false,能够容忍实际 MSG 文件中轻微的结构偏差。- 收件人对象公开
recipient_type用于区分 To、Cc 和 Bcc 条目。
常见问题
| 问题 | 原因 | 修复 |
|---|---|---|
对看起来有效的文件执行msg_exception | 文件不是合法的 OLE CFB 容器 | 使用cfb_reader::from_file()进行验证 — 如果它也抛出异常,则文件不是有效的 MSG |
body() 返回空字符串 | 消息仅使用 HTML 正文 | 使用 html_body() 替代 |
recipients() 为空 | 较旧的 MSG 没有收件人表 | 通过 get_property_value() 使用 common_message_property_id::display_to 访问 display_to |
| Windows 上的流损坏 | 流在未使用 std::ios::binary 的情况下打开 | 向 std::ifstream 添加 std::ios::binary 标志 |
| 大文件的高内存使用 | 所有附件数据在一开始就已加载 | 使用 msg_reader 进行低级访问,而无需完整实例化 |
FAQ
from_file() 会保持文件句柄打开吗?
不。文件在打开后被完整解析,并在方法返回前关闭。调用后可以移动或删除源文件。
我如何判断附件是普通文件还是嵌入的 MSG?
调用 attachment.is_embedded_message()。对嵌入的 MSG 文件返回 true;对常规文件附件返回 false。对于嵌入的消息,可通过 attachment.embedded_message 访问嵌套对象。
from_file() 与使用 msg_reader 有何区别?
mapi_message::from_file() 是高级 API——它解析完整消息并将属性以强类型字段公开。msg_reader 是低级读取器,提供对原始 CFB 容器结构的访问,当您需要在不解析所有 MAPI 属性的情况下检查消息布局时非常有用。
是否同时支持 Unicode 和 ANSI 的 MSG 文件?
是的。mapi_message 同时处理 Unicode(属性类型 ptyp_string)和 ANSI(ptyp_string8)字符串属性。unicode_strings() 对使用 Unicode 属性存储的消息返回 true。
API Reference 摘要
| 类 / 方法 | 描述 |
|---|---|
mapi_message::from_file(path) | 从 std::filesystem::path 加载 MSG 文件 |
mapi_message::from_file(path, strict) | 加载时使用严格模式选项 |
mapi_message::from_stream(stream) | 从 std::istream 加载 MSG |
mapi_message::from_stream(stream, strict) | 使用严格模式选项从流加载 |
mapi_message::subject() | 返回消息主题字符串 |
mapi_message::body() | 返回纯文本正文 |
mapi_message::html_body() | 返回 HTML 正文(如果不存在则为空) |
mapi_message::sender_name() | 返回发件人的显示名称 |
mapi_message::sender_email_address() | 返回发件人的电子邮件地址 |
mapi_message::internet_message_id() | 返回 RFC 2822 Message-ID 头部 |
mapi_message::message_class() | 返回 MAPI 消息类字符串 |
mapi_message::recipients() | 返回收件人列表 |
mapi_message::attachments() | 返回附件列表 |
mapi_attachment::is_embedded_message() | 如果附件是嵌入式 MSG,则为 True |
mapi_message::unicode_strings() | 如果消息使用 Unicode 字符串属性,则为 True |