完整的工作範例可在 GitHub 上取得: manage-xmp-in-psd-and-ai-files-python
介紹
行銷團隊將 400 個 PSD 檔案放入您的資產平台。上傳可以成功,搜尋卻失敗,因為檔案中沒有關鍵字,一半缺少版權聲明,設計師名稱只存在於某個試算表中。解決方案不是更大的試算表。XMP 管理是 GroupDocs.Metadata 在 Python 透過 .NET 的功能,能讀寫嵌入於 Photoshop PSD 與 Illustrator AI 檔案中的 metadata 封包,這意味著所有權與搜尋資料可以直接存放在檔案本身。
XMP 是位於二進位容器內的 XML 封包,依照不同的 schema 組織:Dublin Core 為所有系統皆能理解的欄位,Photoshop schema 提供編輯上下文,XmpBasic 為工具身分。手動解析 PSD 以取得該封包相當困難。使用 Metadata 類別只需要三個屬性查詢,且相同程式碼同樣適用於 AI 檔案。
本教學以四個步驟完整說明往返流程:快照整個封包、讀取重要的 schema、寫入版權與創作者、以及標記關鍵字以供搜尋。每段程式碼皆取自可執行的儲存庫,並驗證寫入的值會持續存在。
前置條件
開始之前,請確保您已具備:
- Python 3 與 pip
- 透過 .NET 的 GroupDocs.Metadata for Python(儲存庫固定使用 26.5 版)
- 一個可供實驗的 PSD 或 AI 檔案
安裝
pip install groupdocs-metadata-net==26.5
步驟 1 - 快照整個 XMP 封包
先查看檔案攜帶的所有資訊。此快照會遍歷根封包、每個已註冊的 schema,最後掃描屬性樹中任何非標準項目,將所有內容收集到一個平面字典中。
result = {}
def put(props, prop):
value = (str(prop.interpreted_value) if prop.interpreted_value is not None
else (str(prop.value) if prop.value is not None else ""))
props[prop.name] = value
with Metadata("campaign-hero.psd") as metadata:
root = metadata.get_root_package()
xmp = getattr(root, "xmp_package", None)
if xmp is not None:
for p in xmp: # root packet properties
put(result, p)
schemes = xmp.schemes
for scheme in (schemes.dublin_core, schemes.xmp_basic, schemes.photoshop,
schemes.camera_raw, schemes.paged_text,
schemes.xmp_dynamic_media, schemes.xmp_media_management):
if scheme is None:
continue
for p in scheme:
put(result, p)
for p in metadata.find_properties(lambda p: p.name is not None):
if p.name not in result: # catch custom packets
put(result, p)
重點說明:
- 先使用
interpreted_value:日期與列舉會以可讀形式返回,而非原始值。 - 七個 schema 加上掃描:最後的
find_properties會捕捉命名 schema 未涵蓋的供應商封包。 - 只開啟一次檔案:整個快照只需一次
Metadata內容上下文,對大量匯入非常重要。
рџ’Ў 提示:在匯入時將此字典建立索引,之後的大多數 metadata 查詢都會變成字典查找,而不必再次讀取檔案。
我的整合應該先讀哪個 XMP schema?
先從 Dublin Core 開始。它的九個 dc: 欄位包含標題、創作者、權利與主題等資訊,這些是大多數 DAM 系統、搜尋索引與授權檢查所共通認可的,且 PSD 與 AI 檔案皆以相同方式呈現。接著讀取 Photoshop schema,以取得城市、版權說明、建立日期等編輯上下文。最後的完整封包掃描則留給必須捕捉全部資訊的匯入工作。
步驟 2 - 讀取解答實際問題的 Schemas
在請求時的程式碼只需聚焦於單一 schema。Dublin Core 能回答所有權與搜尋相關的問題:
dc_fields = {}
with Metadata("campaign-hero.psd") as metadata:
xmp = getattr(metadata.get_root_package(), "xmp_package", None)
dc = xmp.schemes.dublin_core if xmp is not None else None
if dc is not None:
for p in dc:
dc_fields[p.name] = (str(p.interpreted_value)
if p.interpreted_value is not None else
str(p.value) if p.value is not None else "")
print(dc_fields.get("dc:rights", "<no rights recorded>"))
Photoshop schema 以相同方式透過型別化屬性存取:ps.color_mode、ps.icc_profile、ps.city、ps.country、ps.date_created、ps.caption_writer、ps.credit、ps.source,每個屬性皆以 None 防護。這些欄位正是 Bridge、Lightroom 與 DAM 搜尋過濾器在 Adobe 檔案上所依賴的。
注意在沒有 XMP 的檔案上會發生什麼:防護會產生空字典,而不會拋出例外。新匯出的資產常會出現此情況,請在整合中保留此行為。
相同的三個查詢同樣適用於 Illustrator 檔案。只要把 campaign-hero.psd 換成 brand-mark.ai,其他皆不變,這正是混合 Adobe 檔案庫能使用單一路徑程式碼的原因。實務上,剛匯出的 AI 檔案往往比 Photoshop 儲存的檔案填充的 schema 更少,因此空字典路徑會更常被觸發。
步驟 3 - 寫入版權與創作者
現在來看寫入流程。所有權標記會觸及三個欄位,讓每個讀取者看到相同的身分資訊:dc:rights 為法律聲明,dc:creator 為有序列表,xmp:CreatorTool 為讀取 XmpBasic schema 時使用的工具名稱。我曾因資產的版權橫幅顯示「未知作者」而浪費整個下午,原因是值只寫在 dc:creator,而工具只讀取 xmp:CreatorTool。兩者同時寫入即可解決此類問題。
with Metadata("campaign-hero.psd") as metadata:
root = metadata.get_root_package()
xmp = getattr(root, "xmp_package", None)
if xmp is None: # file has no XMP at all
root.xmp_package = XmpPacketWrapper()
xmp = root.xmp_package
if xmp.schemes.dublin_core is None:
xmp.schemes.dublin_core = XmpDublinCorePackage()
dc = xmp.schemes.dublin_core
dc.set_rights("(C) 2026 GroupDocs Sample")
dc.set("dc:creator", XmpArray.from_(["Digital Asset Team"],
XmpArrayType.ORDERED))
if xmp.schemes.xmp_basic is None:
xmp.schemes.xmp_basic = XmpBasicPackage()
xmp.schemes.xmp_basic.creator_tool = "Digital Asset Team"
metadata.save("campaign-hero-stamped.psd")
重點說明:
- 防護會建立缺失的層級:
XmpPacketWrapper與XmpDublinCorePackage會在需要時即時建立,因而支援完全沒有 XMP 的檔案寫入。 - ORDERED 陣列用於創作者:作者順序具有意義,故使用有序的
XmpArray。 - 儲存至新路徑:原始檔案保持不變,這是匯出步驟的正確預設行為。
步驟 4 - 標記關鍵字以供搜尋
dc:subject 是 DAM 搜尋索引使用的關鍵字集合。寫入時一次性取代整個集合:
with Metadata("campaign-hero.psd") as metadata:
root = metadata.get_root_package()
xmp = getattr(root, "xmp_package", None)
if xmp is None:
root.xmp_package = XmpPacketWrapper()
xmp = root.xmp_package
if xmp.schemes.dublin_core is None:
xmp.schemes.dublin_core = XmpDublinCorePackage()
xmp.schemes.dublin_core.set(
"dc:subject",
XmpArray.from_(["landscape", "sunset", "commercial"],
XmpArrayType.UNORDERED))
metadata.save("campaign-hero-tagged.psd")
關鍵字使用 UNORDERED 陣列,因為對索引器而言順序並不重要。而且 set 會直接取代現有集合,若需要累加標記,請先讀取目前的關鍵字,再在 Python 中合併後寫回。
要驗證寫入是否成功,只需對輸出檔案重新執行步驟 2 的讀取程式。此儲存庫已自動化此流程:它會重新讀取輸出,並斷言版權字串與第一個關鍵字仍然存在於已儲存的位元組中。
真實案例應用
DAM 匯入
對每個進入的檔案執行步驟 1 的快照,並將字典與資產記錄一起存放。之後的搜尋、去重與權利檢查皆可直接對資料庫查詢,而不必重新開啟二進位檔案。儲存庫中小樣本 PSD 的快照已在一次遍歷中返回大量屬性,當輸入規模擴大至數千檔案時,同樣的呼叫仍能保持效能。
授權執行
在資產上傳至客戶入口前,必須確保 dc:rights 不為空。未通過檢查的檔案會自動套用步驟 3 的標記流程,確保所有檔案皆帶有版權聲明。
批次重新標記
當分類法變更時,讀取每個檔案的 dc:subject,在 Python 中將舊詞彙映射為新詞彙,然後使用步驟 4 寫回合併後的集合。PSD 與 AI 檔案皆可使用相同迴圈,無需 Photoshop 授權。
最佳實踐與提示
- 將空視為正常:沒有 XMP 的檔案是常態,而非錯誤;提前返回的模式可讓管線持續運作。
- 寫入關鍵字前先合併:
set會取代dc:subject,因此累加標記必須先讀取、擴充再寫入。 - 在兩個 schema 中寫入身分:同時寫入
dc:creator與xmp:CreatorTool可讓 Dublin Core 與 XmpBasic 讀取者保持一致。 - 以讀回驗證寫入:儲存後立即重新讀取成本低,能即時捕捉容器層面的異常。
- 生產環境需授權:評估模式可執行本文示範的所有操作;在正式為客戶資產加蓋標記前請先取得授權。
結論
在 Adobe 檔案中讀寫 XMP 只需三個步驟:透過 get_root_package() 取得封包、對所需的 schema 加上防護,然後讀取或寫入型別化值。掌握這三個步驟後,您即可在本教學中完成完整的往返流程:從封包快照到 schema 讀取,再到版權標記與關鍵字標記,且相同程式碼同時支援 PSD 與 AI 檔案。
準備好在您的專案中實作了嗎?以下是後續建議步驟:
- 閱讀 Working with XMP metadata 文件,了解如何更新與移除封包
- 參考 integration-focused use case guide 以相同儲存庫為基礎的整合案例
- 克隆 sample project 並在自己的資產上執行
其他資源
- GroupDocs.Metadata Documentation
- API Reference
- Sample Projects on GitHub
- GroupDocs.Metadata Blog Category
有關 XMP 工作流程的問題嗎?請在 support forum 提問。