Conversation Thread 是对具有公共线程主题的消息的一系列回复。对话中的消息可以以各种方式显示,例如按层次或时间顺序。要显示消息线程,电子邮件应用程序会识别消息回复。最流行的电子邮件文件格式提供此功能。 对话线程使读者可以快速了解对话的整体结构,突出对话的某些要点,并分析重要信息。 在本文中,我们将重点介绍使用 Aspose.Email 的 PST/MAPI 功能按对话查找和分组邮件。为此,我们将实现一个示例代码,该代码将遍历给定文件夹中的消息,按对话对它们进行分组,然后将每个对话保存到单独的磁盘目录中。
用于支持对话线程的 MAPI 属性
由于 pst 中的消息存储为一组 MAPI 属性,因此我们需要定义与收集消息回复相关联的 MAPI 属性。
这在 Microsoft Docs 部分 中有描述。
可以看出,PidTagConversationIndex 属性允许准确地确定消息是否与某个对话相关联。该属性还指示对话线程中的相对消息位置。 访问页面 了解有关 PidTagConversationIndex
属性的更多信息。标头是 PidTagConversationIndex
属性值的前 22 个字节。它是确定消息是否属于某个会话线程的数据部分。
C# .NET API 读取 Outlook PST 文件
要读取 PST 文件,我们将使用 Aspose.Email for .NET。这是一个使用 .NET 实现电子邮件处理应用程序的了不起的库。使用该库,您可以轻松处理许多不同的电子邮件文件格式。您可以通过 NuGet 或 下载 其 DLL 安装 Aspose.Email for .NET。
PM> Install-Package Aspose.Email
按对话线程对 PST 中的消息进行分组
要按对话对 PST 中的消息进行分组,我们需要以下内容:
- 首先,创建一个“ConversationThread”类。它是一个容器,用于对对话中的消息进行分组。
- 然后,创建一种按对话搜索和分组消息的方法。
- 最后,创建一个将对话线程保存到单独目录的方法。
创建一个 ConversationThread 类
它将具有以下属性。
Id
:会话索引标头的字符串表示形式(22 个字节)。Messages
:对话线程中的消息 ID 列表。
///<summary>
/// 表示对话线程中的消息。
///</summary>
public class ConversationThread
{
private string id;
private List<string> messages;
///<summary>
///初始化一个新的实例<see cref="ConversationThread"/>班级。
///</summary>
///<param name="id">会话索引标头的字符串表示形式。</param>
public MessageThread(string id)
{
this.id = id;
this.messages = new List<string>();
}
///<summary>
/// 获取或设置会话索引头的 HEX 字符串表示(22 字节)。
///</summary>
public string Id { get => id; set => id = value; }
///<summary>
/// 获取对话线程中的消息 ID 列表。
///</summary>
public List<string> Messages { get => messages; }
}
创建一种按对话搜索和分组消息的方法
创建 ConversationThread
类后,我们可以专注于编写一个执行以下操作的方法:
- 浏览文件夹中的所有邮件。出于性能原因,我们将仅使用 EnumerateMessagesEntryId 方法读取消息标识符。
- 对于每条消息,我们将使用 ExtractProperty 方法提取
PidTagConversationIndex
属性。 PidTagConversationIndex
属性值的前 22 个字节相同的消息属于同一个会话。我们将消息 Id 添加到相应ConversationThread
类实例的Messages
属性表示的列表中。- 返回
ConversationThread
实例的列表。
using Aspose.Email.Mapi;
using Aspose.Email.Storage.Pst;
public List<ConversationThread> GroupMessagesByThread(PersonalStorage pst, FolderInfo folder)
{
// 结果
var list = new List<ConversationThread>();
foreach (var entryId in folder.EnumerateMessagesEntryId())
{
// 提取 PidTagConversationIndex 属性及其标头值(22 个字节)。
var convIndexTag = KnownPropertyList.ConversationIndex.Tag;
var convIndexProperty = pst.ExtractProperty(Convert.FromBase64String(entryId), convIndexTag);
var convIndexHeader = (convIndexProperty != null && convIndexProperty.Data != null) ? convIndexProperty.Data.Take(22).ToArray() : null;
var convIndexHeaderString = convIndexHeader != null ?
BitConverter.ToString(convIndexHeader).Replace('-', '') : null;
if (convIndexHeaderString != null)
{
var convThread = list.Find(x => x.Id == convIndexHeaderString);
if (convThread == null)
{
convThread = new ConversationThread(convIndexHeaderString);
convThread.Messages.Add(entryId);
list.Add(convThread);
}
else
{
convThread.Messages.Add(entryId);
}
}
}
return list.Where(x => x.Messages.Count > 1).ToList();
}
创建将对话线程保存到单独目录的方法
最后,让我们将对话保存在目录中。
对于每个 ConversationThread
实例,请执行以下操作:
- 使用线程主题的名称创建一个单独的目录。
- 枚举
ConversationThread.Messages
属性中的标识符,对于每个标识符,使用 ExtractMessage 方法提取一条消息,并使用 Save 方法将消息保存在创建的目录中。
public void SaveThreads(List<ConversationThread> list, string outDirectory)
{
var invalidChars = Path.GetInvalidFileNameChars();
foreach (var item in list)
{
string? threadDirectory = null;
var i = item.Messages.Count - 1;
foreach (var entryId in item.Messages)
{
var msg = pst.ExtractMessage(entryId);
if (threadDirectory == null)
{
var threadName = new string(msg.ConversationTopic.Where(x => !invalidChars.Contains(x)).ToArray()).Trim();
threadDirectory = Path.Combine(outDirectory, threadName);
Directory.CreateDirectory(threadDirectory);
}
msg.Save(Path.Combine(threadDirectory, $"{i--}.msg"));
}
threadDirectory = null;
}
}
获取免费 API 许可证
您可以使用 免费临时许可证 使用 Aspose.Email for .NET,而不受评估限制。
结论
本文展示了如何使用 Aspose.Email 在 PST 中搜索与对话相关的消息。 通过进一步探索 PidTagConversationIndex 文档,您还可以通过添加例如对话消息的分层排序来使实现复杂化。您可以使用 文档 了解有关 Aspose.Email 的更多信息。如果您有任何问题,可以发帖到我们的 论坛。