完整的工作示例可在 GitHub 上获取:
使用 GroupDocs.Metadata .NET 编辑 PSD 和 AI 文件中的 XMP
生产挑战:隐藏在无人关注处的元数据
一家品牌工作室交付了一个活动:分层的 PSD 主文件、AI 源文件、数百个资产推送到客户的 DAM。三周后,授权部门询问谁拥有主图像。答案是存在的,但它只在电子邮件线程中,因为文件本身的 dc:rights 字段为空。XMP 编辑是 GroupDocs.Metadata 在 .NET 上的功能,能够在流水线层面解决此类问题,直接在 PSD 和 AI 文件内部读取和写入 XMP 元数据包,而无需任何 Adobe 软件。
规模问题悄然出现。单个设计师可以手动、仔细地填写元数据面板,但只能维持一段时间。每季度处理数千个资产的制作团队无法做到,而且每一次在代理、工作室和客户之间的交接都会使未被验证的 PSD 元数据文件数量倍增。搜索不再能找到存在的资产。版权问题变成了考古,而考古没有服务水平协议。
这些团队真正需要的东西看起来像一个 API 规范:在摄取时快照文件携带的全部信息,在授权关卡检查特定字段,并在导出时写入所有权和关键字,对两种 Adobe 格式使用相同的代码。
现实检查:dc:rights 字段为空的资产并不意味着未授权,但下游没有人能在找不到记得此事的人的情况下证明相反。
常规解决方案为何不足
团队通常在实现自动化之前尝试三种方法:
- 在 Photoshop 或 Bridge 中手动编辑面板:对每个文件有效,无法审计,并且需要 Adobe 许可证来完成本质上是数据录入的任务。
- 伴随的电子表格:元数据虽然存在,但在文件被复制、重命名或重新交付的瞬间就与资产脱离。
- 内部解析:PSD 资源块和 AI 容器格式复杂,且自制解析器在 Adobe 对任何内容进行修改的第一时间就会成为维护负担。
GroupDocs.Metadata 通过一个 API 填补了这一空白:将根包转换为 IXmp,即可对两种格式的包进行读取和写入,同时支持文档中列出的 170 多种其他格式的元数据。
解决方案:在流水线中进行 XMP 操作
GroupDocs.Metadata for .NET 在资产流水线的三个环节中发挥作用。摄取时,它将完整的包快照存入字典,以便数据库索引。授权关卡时,它读取 Dublin Core(dc:rights 和 dc:creator 所在的方案)。导出时,它写入所有权和 dc:subject 关键字,并为完全没有 XMP 的文件创建缺失的方案。我已经参加了太多次发布回顾,根本原因都是资产缺少版权数据;设置关卡的原因是回顾的成本远高于读取的成本。
要跟进实现,您需要:
- .NET SDK 8.0 或更高版本
- GroupDocs.Metadata 26.6.0(获取临时许可证)
- 一个用于实验的 PSD 或 AI 文件
dotnet add package GroupDocs.Metadata --version 26.6.0
配套仓库 为每种格式提供了一个示例,并验证了下面的每一步。
逐步实现工作流
步骤 1 — 在摄取时快照全部内容
一次遍历即可捕获包、已命名的方案以及任何供应商工具隐藏的内容。将字典与资产记录一起存储,后续的查询就可以直接在数据库中查找。
// Full XMP snapshot: packet, schemes, then a deep sweep
var result = new Dictionary<string, string>();
using (var metadata = new Metadata(adobeFilePath))
{
var root = metadata.GetRootPackage() as IXmp;
if (root?.XmpPackage == null) return result;
foreach (var property in root.XmpPackage)
{
result[property.Name] = property.InterpretedValue?.ToString()
?? property.Value?.ToString() ?? string.Empty;
}
// CollectScheme(...) repeats this loop for DublinCore, XmpBasic,
// Photoshop, CameraRaw, PagedText, XmpDynamicMedia, XmpMediaManagement
foreach (var p in metadata.FindProperties(p => p.Name != null))
{
if (!result.ContainsKey(p.Name))
{
result[p.Name] = p.InterpretedValue?.ToString()
?? p.Value?.ToString() ?? string.Empty;
}
}
}
return result;
InterpretedValue 在整个过程中优先出现,因此日期和枚举会以人类可读的形式呈现。随后使用 FindProperties 的遍历确保了对 Adobe Illustrator 元数据的完整性,即使这些元数据是由未被已知方案覆盖的插件写入的。
在大规模摄取时有两点运营注意事项。将快照以资产 ID 为键存储,并标记捕获日期,因为文件可能会变化,而快照是您的“之前”图像。将空字典视为信号而非错误;它会将资产直接路由到标记步骤,而不是导致摄取失败。
步骤 2 — 在授权关卡检查 Dublin Core
九个 dc:* 字段回答了法律和授权实际提出的问题。仅读取该方案可保持关卡的快速响应。
// dc:* fields only - Title, Creator, Rights, Subject and friends
var result = new Dictionary<string, string>();
using (var metadata = new Metadata(adobeFilePath))
{
var root = metadata.GetRootPackage() as IXmp;
var dc = root?.XmpPackage?.Schemes?.DublinCore;
if (dc == null) return result;
foreach (var property in dc)
{
result[property.Name] = property.InterpretedValue?.ToString()
?? property.Value?.ToString() ?? string.Empty;
}
}
return result;
这些设置对生产团队的重要性:
- 空值条件链:在全新导出中,缺少 XMP 的文件是常规情况;空字典表示“标记我”,而不是“崩溃”。
- 方案范围:关卡在每次资产移动时运行,因此读取九个字段而不是整个树结构可以保持低成本。
步骤 3 — 在导出时标记所有权
写入操作涉及三层,确保每个读取器(无论是否支持 XMP)都看到相同的身份。首先使用守卫创建缺失的包和方案对象。
// Guard-create the packet and scheme, then write rights and creator
using (var metadata = new Metadata(inputPath))
{
var root = metadata.GetRootPackage() as IXmp;
if (root == null) return;
if (root.XmpPackage == null)
root.XmpPackage = new XmpPacketWrapper();
if (root.XmpPackage.Schemes.DublinCore == null)
root.XmpPackage.Schemes.DublinCore = new XmpDublinCorePackage();
var dc = root.XmpPackage.Schemes.DublinCore;
dc.SetRights(copyright);
dc.Set("dc:creator", XmpArray.From(new[] { creator }, XmpArrayType.Ordered));
// Mirror the identity for XmpBasic readers and tag-classified fields
if (root.XmpPackage.Schemes.XmpBasic == null)
root.XmpPackage.Schemes.XmpBasic = new XmpBasicPackage();
root.XmpPackage.Schemes.XmpBasic.CreatorTool = creator;
metadata.SetProperties(p => p.Tags.Contains(Tags.Person.Creator),
new PropertyValue(creator));
metadata.Save(outputPath);
}
使用 Tags.Person.Creator 的 SetProperties 调用是值得借鉴的细节:它会更新库分类为创作者字段的所有属性,无论格式将其存储在哪里,因此即使从未读取 XMP 的工具也能显示正确的名称。
步骤 4 — 为搜索写入关键字
dc:subject 是 DAM 搜索索引的词汇表。没有它,资产虽然存在,却永远匹配不到查询。
// Replace the dc:subject bag with the pipeline's keyword list
root.XmpPackage.Schemes.DublinCore.Set(
"dc:subject",
XmpArray.From(keywords, XmpArrayType.Unordered));
metadata.Save(outputPath);
写入操作会替换现有的 bag,因此增量标记的流程是读取、在 C# 中合并、再写入。仓库写入了三个示例关键字,并断言第一个在保存的字节中保留下来。对词汇表进行版本管理的团队通常会为每个活动存储规范的关键字集合,并让流水线在每次导出时将文件与之对齐,这样关键字漂移就变成了差异而不是争论。
我们是否需要 Photoshop 许可证仅仅来修复元数据?
不需要,这通常也是自动化的目的所在。GroupDocs.Metadata 在 .NET 中直接读取和写入包,因此服务器端作业可以在不打开任何 Adobe 应用的情况下为整个档案标记版权或修复关键字。设计师保留他们的工具用于设计工作,而流水线则负责大规模的元数据卫生。
这对业务的影响
上述工作流将三类常见事件转变为非事件。版权问题不再需要人工记忆,因为在关卡检查并在缺失时标记 dc:rights。不可搜索的资产不再累积,因为关键字由流水线写入,而不是由记得的人手动添加。元数据工作也不再占用 Adobe 许可证,因为四个步骤都不打开设计工具。
这里还有一个手动编辑永远无法提供的审计故事。每一次关卡决策和每一次标记都会记录在代码路径中,因此当客户询问资产的版权信息是如何产生时,答案是一条带时间戳的流水线记录。整个实现仅由五个小方法组成,并在配套仓库中针对 PSD 和 AI 示例进行了断言,这正是平台团队可以在没有专职维护者的情况下拥有的足迹。
结论
一旦流水线掌控了 PSD 和 AI 文件中的 XMP 元数据,它就不再是手动的繁琐工作:在摄取时快照,在关卡检查 Dublin Core,在导出时标记所有权和关键字。一次 IXmp 转换即可兼容两种格式,守卫确保新导出文件安全输入,本文展示的每个操作都在示例仓库中得到断言。
准备好将其接入您的流水线了吗?
- 克隆示例仓库并运行
dotnet run - 按照技术深度用例指南进行操作
- 阅读使用 XMP 元数据的参考文档