💡 完整可运行示例已在 GitHub 上提供:
scrub-office-document-pii-dotnet
旧方法令人痛苦
常规流程是这样的:打开文档 → 文件 → 信息 → 检查问题 → 检查文档 → 勾选复选框 → 删除全部 → 另存为新名称 → 关闭 → 打开下一个。四十个文件后,有人注意到“检查文档”也把记录索引键所依赖的 Title 删除了,而一个小时前发送的副本仍然保留了 SharePoint 审批人 ID,因为该文件是从另一个会把该字段写回的应用程序保存的。
元数据 PII 删除是 GroupDocs.Metadata 在 .NET 中的功能,可以编程方式删除 Office 文档中携带身份信息的属性,并报告随后剩余的内容。手动流程在三方面失败:无法扩展到大量文件、对要删除的字段只能全有全无、且不会生成已删除内容的记录。本文展示了相同工作在 .NET 中的实现方式,一次处理一个属性组。
了解实际存在哪些信息很有帮助。经过一次审阅的 Word 文件通常包含 Author 和 LastSavedBy(保存者的 Windows 账户),Manager 和 Company(企业模板),修订计数器,TotalEditingTime,LastPrinted 时间戳,以及评论线程计数。若再加入 SharePoint,还会出现审批人标识、工作流路径、内容类型 URI,以及文档创建时使用的模板。所有这些信息在页面上不可见,却全部存储在同一个文件中。
有更好的方法
GroupDocs.Metadata for .NET 中的所有操作都通过同一个属性搜索引擎完成。RemoveProperties 接受一个针对 MetadataProperty 的 lambda,删除 lambda 接受的每个属性,并返回删除的数量。FindProperties 在不写入的情况下运行相同的 lambda。属性还携带标签,Tags.Person.Creator 能在不同格式和包中识别作者类字段,而无需匹配各生产应用程序不同的字面名称。
这就提供了三种清理方式,而不是单一按钮:针对身份字段的标签遍历、针对如评论和修订等族的名称遍历,以及在不应保留任何内容时使用 Sanitize()。这三种方式都会返回数字,而这些数字正是使遍历可审计的关键。
新方法:每个属性组对应一个谓词
步骤 1 - 清除名称
四个标签检查覆盖身份组。描述性字段保持不变,这正是与文档检查器的“全部删除”不同之处:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Tags.Contains(Tags.Person.Creator) ||
p.Tags.Contains(Tags.Person.Editor) ||
p.Tags.Contains(Tags.Person.Manager) ||
p.Tags.Contains(Tags.Corporate.Company));
metadata.Save(outputPath);
return affected;
}
FileFormat.Unknown 检查是保持返回零真实的保护:没有它,无法读取的文件和干净的文件对调用者看起来是一样的。
步骤 2 - 清除它们周围的族
评论、修订和服务器字段没有标签,因此谓词改为匹配名称。编辑时间线是最常被忘记的组,也是说明文档是如何生成的关键:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.RemoveProperties(p =>
p.Name != null && (
p.Name.Contains("Revision") ||
p.Name.Contains("TrackedChange") ||
p.Name.Contains("LastPrinted") ||
p.Name.Contains("TotalEditingTime") ||
p.Name.Contains("EditTime")));
metadata.Save(outputPath);
return affected;
}
使用子串匹配是有意为之:它能同时捕获 CommentsCount 与 Comment,以及 TotalEditingTime 与 EditTime,而无需为每种格式维护精确的名称列表。SharePoint 的遍历则在同一调用中加入 Server、Workflow、Approver、ContentType 和 Template。
步骤 3 - 全部清除,然后检查结果
在信任边界处,一次调用即可替代上述四次遍历:
using (var metadata = new Metadata(inputPath))
{
if (metadata.FileFormat == FileFormat.Unknown) return 0;
var affected = metadata.Sanitize();
metadata.Save(outputPath);
return affected;
}
接下来是手动流程没有对应的部分。验证扫描通过 FindProperties 复用删除谓词,并将剩余项分为两类列表:
foreach (var p in props)
{
var value = p.InterpretedValue?.ToString() ?? p.Value?.ToString() ?? string.Empty;
if (string.IsNullOrWhiteSpace(value)) continue;
if (value == "0" || value == "0.0") continue;
var entry = $"{p.Name}={value}";
var name = p.Name ?? string.Empty;
if (name.StartsWith("Comment") || name.StartsWith("Revision"))
report.ContentLevelLeaks.Add(entry);
else
report.MetadataLeaks.Add(entry);
}
MetadataLeaks 必须为空,文件才算已消毒。ContentLevelLeaks 仅作信息提示:Word 评论和修订作者位于 word/document.xml 中,属于正文内容,清除这些需要使用如 Aspose.Words 之类的内容编辑库,而不是元数据 API。
为什么不直接对所有内容调用 Sanitize?
因为大多数文档仍在使用中。Sanitize() 会清除检测到的每个包,包括 Title、Subject 和 Keywords——这些是记录系统和搜索索引依赖的字段。文件在内部流转时使用有针对性的遍历,保留描述性元数据;而真正离开组织的副本则使用完整清除。
对比:前后对照
| 手动检查 | GroupDocs.Metadata for .NET | |
|---|---|---|
| 选择性 | 删除全部,包含描述性字段 | 每个属性组对应一个谓词 |
| 覆盖范围 | 对话框中暴露的字段 | 库检测到的每个包,包含自定义 OOXML 部分 |
| 记录 | 无 | 每次操作返回受影响计数 |
| 验证 | 重新打开并查看 | 使用 FindProperties 扫描,生成元数据和内容级别列表 |
| 200 文件批处理 | 200 次点击 | 单次循环,五个操作,每个文件一行日志 |
改变行为的那一行是“记录”。一旦每次遍历返回计数,消毒就不再是某人记得要做的步骤,而是管道可以断言的数据:测试阈值、审计表字段、夜间作业的失败条件。这也是手动流程在任何纪律层面都无法实现的行。
实际案例:发送前钩子
支持门户允许工作人员将文档附加到客户工单。附件处理程序现在在文件存储前先运行身份遍历和服务器遍历,将两次计数记录到工单上,并对已保存的副本执行泄漏检查。非空的 metadata‑leak 列表会以指明违规属性的消息拒绝上传,使得附加文件的人员能够立即得知问题,而不是等到文件到达客户后才发现。
有两个细节让该钩子实用。遍历写入新路径,原始文件仍保留在工作人员自己的存储中,自动步骤不会毁掉任何内容。计数写入工单记录并与附件并列,这意味着“此文档删除了哪些内容”可以通过存储的数字来回答,而不是对管道通常行为的猜测。
我第一次在真实模板上使用该检查时,返回了一个 Manager 值——该值在身份遍历几秒前已被删除,但企业模板在保存时又写回来了。删除调用完全符合文档,问题出在其周围的管道,而只有读取回来的结果才揭示了这一点。
还能用 GroupDocs.Metadata 做什么?
相同的谓词引擎还能读取。比较同一文档两个版本之间的属性可以发现所有权变更和审阅过程之外的重新作者,而 metadata scrubbing overview 则说明了交互式工具仍然可以与 API 驱动的遍历并存。由于标签系统跨格式,本文编写的身份谓词同样适用于 PDF、图像和音频文件,无需修改。
这种可移植性值得提前规划。以 MetadataProperty 为 lambda 编写的清理规则就是普通的 C#,可以放在共享库中,对固定文档进行单元测试,并由任何需要的服务调用:导出端点、计划的记录作业,或在发布前对文档附件进行消毒的构建步骤。规则集中管理,调用点只需改变。
结论
四个有针对性的遍历、一次完整消毒、一次验证扫描。这套组合覆盖了 Office 文档的实际需求:文件流转时保留描述性元数据,离开组织时全部清除,并始终能够证明结果。克隆示例项目,对经过真实审阅的文档运行它,查看受影响计数。通常会高于预期,这正是本文的核心要点。