完整的工作範例可在 GitHub 上取得:
read-and-write-xmp-in-psd-ai-files-java

舊方法很痛苦

想像一下沒有人願意自願的檔案庫清理。一個放著 PSD 主檔與 AI 原始檔的資料夾在進入 DAM 前需要加入權利聲明與關鍵字。常規流程:在 Photoshop 中開啟檔案、開啟「檔案資訊」、輸入版權、輸入關鍵字、儲存、關閉、下一個檔案。每次儲存都會重新渲染圖層檔案,只是為了改變幾個字串的 XMP 中繼資料。讓我學到這課的檔案庫是一堆未標記的 Illustrator 檔,沒有人能搜尋;我們用迴圈解決,而不是靠更多耐心。

把這個常規流程乘以一個檔案庫,它就不再是個任務,而是變成一個專案。更糟的是,它無法稽核:沒有人能事後證明哪些檔案已處理,而被跳過的檔案看起來與其他檔案相同,直到授權問題把它們找出來。面板方式也悄悄把資料輸入與設計工具耦合在一起。要修正中繼資料的人必須擁有 Adobe 授權、能舒適開啟圖層主檔的工作站,還要有耐心等候因改變字串而重新渲染的儲存。

手動操作的真正成本:手工修正的中繼資料是之後沒人能驗證的中繼資料;這個流程除了讓設計師疲憊之外,沒有留下任何痕跡。

有更好的方法

GroupDocs.Metadata for Java 直接讀寫 XMP 封包。將 getRootPackage() 轉型為 IXmp,封包、其方案與陣列都是普通的 Java 物件,對 PSD 與 AI 容器完全相同,且不需要 Adobe 軟體。文件說明 列出 170 多種格式共用同一套 API。

在開始之前,您需要:

  • JDK 8 或更新版本,搭配 Maven
  • GroupDocs.Metadata for Java 24.7(取得臨時授權)
  • 一個用來練習的 PSD 或 AI 檔案

將相依性與 GroupDocs 倉庫加入 pom.xml:

mvn dependency:get -Dartifact=com.groupdocs:groupdocs-metadata:24.7

伴隨的倉庫 內含可直接使用的 pom.xml 以及兩種格式的範例檔,並驗證以下每一步。

新方法:Java 中的四個操作

步驟 1 — 查看檔案攜帶了什麼

快照會把封包與每個方案全部倒出到一個 LinkedHashMap,保留檔案宣告的順序。

// Snapshot the packet, then sweep for anything the schemes missed
Map<String, String> result = new LinkedHashMap<>();
try (Metadata metadata = new Metadata(adobeFilePath)) {
    IXmp root = (IXmp) metadata.getRootPackage();
    if (root != null && root.getXmpPackage() != null) {
        for (MetadataProperty p : root.getXmpPackage()) {
            put(result, p);
        }
        XmpSchemes schemes = root.getXmpPackage().getSchemes();
        collect(result, schemes.getDublinCore());
        collect(result, schemes.getPhotoshop());
        collect(result, schemes.getXmpBasic());
        collect(result, schemes.getCameraRaw());
    }
    for (MetadataProperty p : metadata.findProperties(new NamedPropertySpec())) {
        if (!result.containsKey(p.getName())) put(result, p);
    }
}
return result;

小型的 put 與 collect 輔助方法偏好使用 getInterpretedValue(),讓日期以可讀形式呈現,而基於 Specification 的掃描會捕捉供應商封包。倉庫版本走過七個方案;結構保持不變。

步驟 2 — 讀取回答問題的欄位

授權會問 dc:rights。搜尋會關心 dc:subject。兩者都屬於 Dublin Core,且受限讀取只需九個欄位,無需遍歷整棵樹。

// dc:* only - the interoperability fields DAM systems agree on
XmpDublinCorePackage dc = root.getXmpPackage().getSchemes().getDublinCore();
if (dc == null) return result;
for (MetadataProperty p : dc) {
    String value = "";
    if (p.getInterpretedValue() != null
            && p.getInterpretedValue().getRawValue() != null) {
        value = String.valueOf(p.getInterpretedValue().getRawValue());
    } else if (p.getValue() != null && p.getValue().getRawValue() != null) {
        value = String.valueOf(p.getValue().getRawValue());
    }
    result.put(p.getName(), value);
}

Photoshop 方案的讀取方式相同,透過型別化的 getter(getCity()、getCredit()、getColorMode() 等五個)取得 psd 中繼資料欄位,這些欄位是 Bridge 與 Lightroom 篩選器會讀取的。倉庫中的讀取器為每個 getter 包裝了空值安全的輔助函式,讓稀疏的檔案回傳空字串而不是意外。這個細節比看起來更重要:自動化檔案庫的核心就在於讓異常檔案也能順利通過迴圈,而不是卡住。

兩種受限讀取的成本特性值得說明:一次檔案開啟、一次方案、無樹遍歷。把它們放在請求處理器與閘道中;將完整快照保存下來供後續 ingest 工作使用。

步驟 3 — 在不開啟 Adobe 的情況下蓋章與標記

寫入器會在缺少的情況下建立物件,這讓它們對於全新匯出的、根本沒有封包的檔案也安全。

// Create missing layers, then write rights, creator, and CreatorTool
if (root.getXmpPackage() == null) {
    root.setXmpPackage(new XmpPacketWrapper());
}
if (root.getXmpPackage().getSchemes().getDublinCore() == null) {
    root.getXmpPackage().getSchemes().setDublinCore(new XmpDublinCorePackage());
}
XmpDublinCorePackage dc = root.getXmpPackage().getSchemes().getDublinCore();
dc.setRights(copyright);
dc.set("dc:creator", XmpArray.from(new String[]{creator}, XmpArrayType.Ordered));

if (root.getXmpPackage().getSchemes().getXmpBasic() == null) {
    root.getXmpPackage().getSchemes().setXmpBasic(new XmpBasicPackage());
}
root.getXmpPackage().getSchemes().getXmpBasic().setCreatorTool(creator);

metadata.save(outputPath);

關鍵字遵循相同模式,只需一次呼叫,即以 Unordered 陣列寫入整個 bag:

// Replace the dc:subject bag - merge in Java first for additive tagging
root.getXmpPackage().getSchemes().getDublinCore().set(
        "dc:subject", XmpArray.from(keywords, XmpArrayType.Unordered));
metadata.save(outputPath);

Main.java 完成迴圈:它重新讀取輸出檔,斷言版權字串與第一個關鍵字確實在儲存後仍在。

這個斷言值得特別說明。中繼資料寫入失敗時往往悄悄失效;檔案會儲存、位元組會變化,但您原本想寫入的值可能根本不存在,原因可能是方案物件已過時或路徑指向了原始檔。每次寫入後再讀回一次,只會多一次檔案開啟,但能把「腳本執行完畢」變成「值已存在」,這正是檔案庫擁有者真正想要的訊息。請在正式環境保留此步驟,而不僅僅在示範中使用。

關鍵字實際上如何讓資產可被搜尋?

搜尋工具不會讀取像素,它們讀取 dc:subject。Bridge、DAM 索引器與素材平台都把這個 bag 當作資產的詞彙表,沒有關鍵字的檔案根本不會匹配任何查詢。以 Unordered XmpArray(AddKeywords 的做法)寫入 bag,就是讓資產從「不可見」變成「可搜尋」的關鍵,而寫入只需一次儲存。

並排比較:前後對照

之前(面板編輯) 之後(Java 流程)
工具 每個檔案使用 Photoshop 或 Bridge 單一 Maven 專案,無需 Adobe 授權
覆蓋範圍 面板顯示的欄位 所有方案加上供應商封包
可重複性 取決於誰點擊 相同迴圈、相同結果、可稽核
無 XMP 的檔案 面板行為不一 防護機制會建立封包與方案
驗證 信任 每個檔案皆有斷言回讀

驗證這一列決定了檔案庫的成敗:能自行證明寫入成功的腳本,就是「我們已標記檔案」與「我們可以向您展示」之間的差別。

實務案例:代理機構交接

一家工作室收到三家代理的混合 PSD 與 AI 交付,每家都有自己的中繼資料規範。現在的匯入工作在檔案到達時執行快照,標記 dc:rights 為空的檔案,蓋上合約規定的權利行,並寫入活動關鍵字集合。相同的迴圈同時支援兩種格式,因為程式碼中沒有任何容器名稱,而 Illustrator 的中繼資料若為空,仍會被標記與搜尋。

後續的影響是工作室未曾預料的:代理績效卡。因為匯入工作會記錄哪些交付的權利欄位為空,採購部門得以看出哪些供應商提供乾淨的中繼資料,哪些依賴客戶自行修正。與最差供應商的對話只需要一張圖表。

GroupDocs.Metadata 還能做什麼?

  • 同一檔案中的 EXIF 與 IPTC:PSDs 同時攜帶三種中繼資料標準;同一套函式庫可透過各自的套件讀取另外兩種,讓完整的資產檔案概況只差兩次讀取。
  • 170 多種其他格式:相同的 Metadata 入口點同時支援 Office、PDF、音訊與影片檔,這也是為何單一匯入工作能覆蓋整個混合檔案庫的原因。
  • 屬性搜尋:使用 findProperties 搭配 Specification,可為任何檔案掃描任意自訂條件,從權利檢查到自訂欄位搜尋皆可實現。

結論

過去每個檔案都要手動開面板的流程,現在只需要四個 Java 操作:快照、受限讀取、所有權蓋章、關鍵字寫入。相同的程式碼同時支援 PSD 與 AI 檔,防護機制讓空白匯出安全,且斷言的範例專案在指向真實檔案庫前就已驗證完整套件。

準備好淘汰「檔案資訊」面板了嗎?

其他資源