Pełny działający przykład dostępny na GitHub: read-and-write-xmp-in-psd-ai-files-java

Stara metoda była bolesna

Wyobraź sobie porządkowanie archiwum, do którego nikt nie chce się zgłosić. Folder z plikami PSD i źródłami AI wymaga umieszczenia informacji o prawach autorskich i słowach kluczowych, zanim trafi do systemu DAM. Rutyna: otwierasz plik w Photoshopie, otwierasz „File Info”, wpisujesz informacje o prawach autorskich, wpisujesz słowa kluczowe, zapisujesz, zamykasz, przechodzisz do kolejnego pliku. Każde zapisanie ponownie renderuje warstwowy plik tylko po to, by zmienić kilka ciągów metadanych XMP. Archiwum, które dało mi tę lekcję, było folderem nieoznaczonych plików Illustrator, których nikt nie mógł przeszukać; naprawiliśmy to pętlą, nie większą cierpliwością.

Pomnóż tę rutynę przez archiwum i przestaje być to zadanie, a staje się projektem. Co gorsza, jest nieaudytowalne: nikt nie może później udowodnić, które pliki zostały przetworzone, a te pominięte wyglądają identycznie, dopóki nie pojawi się pytanie o licencję. Metoda panelowa dodatkowo cicho łączy wprowadzanie danych z narzędziami projektowymi. Ktokolwiek naprawia metadane, potrzebuje miejsca w Adobe, stacji roboczej, która komfortowo otwiera warstwowe pliki, oraz cierpliwości, by czekać na zapisy, które ponownie renderują grafikę tylko po to, by zmienić ciągi znaków.

Rzeczywisty koszt ręcznego wykonywania: metadane poprawione ręcznie to metadane, których nikt nie może później zweryfikować; proces nie zostawia śladu, poza zmęczonymi projektantami.

Istnieje lepszy sposób

GroupDocs.Metadata for Java odczytuje i zapisuje pakiet XMP bezpośrednio. Rzutuj getRootPackage() na IXmp, a pakiet, jego schematy i tablice są zwykłymi obiektami Javy, identycznymi dla kontenerów PSD i AI, bez udziału oprogramowania Adobe. Dokumentacja wymienia ponad 170 formatów dostępnych przez to samo API.

Zanim zaczniemy, będziesz potrzebować:

Dodaj zależność i repozytorium GroupDocs do swojego pom.xml:

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

Repozytorium towarzyszące dostarcza gotowy pom.xml oraz przykładowe pliki obu formatów, i weryfikuje każdy poniższy krok.

Nowy sposób: cztery operacje w Javie

Krok 1 – Zobacz, co niesie plik

Migawka zrzuca pakiet i każdy schemat do jednego LinkedHashMap, zachowując kolejność zadeklarowaną w pliku.

// 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;

Małe pomocnicze metody put i collect preferują getInterpretedValue(), dzięki czemu daty są czytelne, a przeszukiwanie oparte na Specification łapie pakiety dostawców. Wersja w repozytorium przegląda siedem schematów; struktura pozostaje taka sama.

Krok 2 – Odczytaj pola, które odpowiadają na pytania

Licencjonowanie pyta o dc:rights. Wyszukiwanie interesuje dc:subject. Oba znajdują się w Dublin Core, a odczyt w zakresie kosztuje dziewięć pól, a nie pełne przeszukiwanie drzewa.

// 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);
}

Schemat Photoshop działa tak samo, korzystając z typowanych getterów (getCity(), getCredit(), getColorMode() i pięciu kolejnych), obejmując pola metadanych PSD odczytywane przez Bridge i filtry Lightroom. W repozytorium ten czytnik owija każdy getter pomocnikiem zabezpieczającym przed null, więc słabo wypełniony plik zwraca puste ciągi zamiast niespodzianek. Ten szczegół ma większe znaczenie, niż się wydaje: cały sens automatyzacji archiwum polega na tym, że nietypowe pliki przechodzą dalej, zamiast zatrzymywać pętlę.

Oba odczyty w zakresie mają podobny profil kosztowy, który warto nazwać. Jeden otwarcie pliku, jeden schemat, brak przeszukiwania drzewa. Umieść je w obsłudze żądań i bramkach; zachowaj pełną migawkę dla zadań ingestujących, które przechowują wszystko.

Krok 3 – Oznacz i otaguj bez otwierania Adobe

Zapisywacze tworzą brakujące elementy w razie potrzeby, co czyni je bezpiecznymi dla świeżych eksportów nieposiadających w ogóle pakietu.

// 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);

Słowa kluczowe podążają tym samym wzorcem przy jednym wywołaniu, zapisując całą torbę jako nieuporządkowaną tablicę:

// 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 zamyka pętlę: ponownie odczytuje wyniki i weryfikuje, że ciąg praw autorskich oraz pierwsze słowo kluczowe faktycznie przetrwały zapis.

To zamknięcie asercji zasługuje na zdanie propagujące. Zapisy metadanych zawodzą cicho, gdy się nie powiodą; plik zostaje zapisany, bajty się zmieniają, a wartość, którą chciałeś zapisać, po prostu nie istnieje, bo obiekt schematu był przestarzały lub ścieżka wskazywała na oryginał. Odczyt zwrotny po każdym zapisie kosztuje jedno dodatkowe otwarcie pliku i zamienia „skrypt zakończył się” w „wartości są obecne”, co jest właśnie tym, czego właściciel archiwum naprawdę chce. Trzymaj to w produkcji, nie tylko w demonstracji.

Krok 4 – Jak słowa kluczowe naprawdę czynią zasoby znajdowalnymi?

Narzędzia wyszukiwania nie czytają pikseli; czytają dc:subject. Bridge, indeksatory DAM i platformy stockowe traktują tę torbę jako słownictwo zasobu, więc plik bez słów kluczowych po prostu nigdy nie pasuje do zapytania. Zapisanie torby jako nieuporządkowanej XmpArray, tak jak robi to AddKeywords, jest tym, co przenosi zasób z niewidzialnego do widzialnego, a zapis kosztuje jedno zapisanie.

Porównanie: przed vs. po

Przed (edycja w panelu) Po (pipeline w Javie)
Narzędzia Photoshop lub Bridge dla każdego pliku Jeden projekt Maven, bez miejsca w Adobe
Zasięg Pola, które udostępnia panel Każdy schemat plus pakiety dostawców
Powtarzalność Zależy od tego, kto kliknął Ta sama pętla, ten sam wynik, audytowalny
Pliki bez XMP Zachowanie panelu jest zmienne Guardy tworzą pakiet i schematy
Weryfikacja Zaufanie Asertywne odczyty zwrotne dla każdego pliku

Wiersz weryfikacji rozstrzyga to dla archiwów: skrypt, który dowodzi własnych zapisów, to różnica między „oznaczyliśmy pliki” a „możemy wam to pokazać”.

Przykład z życia: przekazanie agencji

Studio otrzymuje mieszane dostawy PSD i AI od trzech agencji, z których każda ma własną dyscyplinę metadanych. Ich zadanie przyjęcia teraz uruchamia migawkę przy przybyciu, oznacza pliki, których dc:rights odczytuje pusty, nakłada na nie ustaloną linię praw, i zapisuje zestaw słów kluczowych kampanii. Ta sama pętla obsługuje oba formaty, ponieważ w kodzie nie ma nazwy kontenera, a metadane Adobe Illustrator, które przychodzą puste, zostają otagowane i możliwe do wyszukania.

Efekt uboczny, którego studio nie przewidziało, to karty wyników agencji. Ponieważ zadanie przyjęcia loguje, które dostawy przybyły z pustymi polami praw, dział zakupów widzi, które dostawcy dostarczają czyste metadane, a które polegają na kliencie, by je naprawić. Rozmowa z najgorszym sprawcą zajęła jeden wykres.

Co jeszcze możesz zrobić z GroupDocs.Metadata?

  • EXIF i IPTC w tych samych plikach: PSD‑y zawierają trzy standardy metadanych; ta sama biblioteka odczytuje pozostałe dwa poprzez własne pakiety, więc pełny profil zasobu jest o dwa odczyty dalej.
  • Ponad 170 innych formatów: identyczny punkt wejścia Metadata obsługuje pliki Office, PDF, audio i wideo, co pozwala jednemu zadaniu przyjęcia objąć cały mieszany archiwum.
  • Wyszukiwanie właściwości: findProperties z Specification przeszukuje dowolny plik pod kątem dowolnego predykatu, od kontroli praw po poszukiwania pól niestandardowych.

Podsumowanie

To, co kiedyś było rutyną panelową dla pojedynczego pliku, stało się teraz czterema operacjami w Javie: migawka, odczyt w zakresie, znacznik własności, zapis słów kluczowych. Ten sam kod obsługuje pliki PSD i AI, guardy czynią puste eksporty bezpiecznymi, a asertywny projekt przykładowy dowodzi całego zestawu, zanim skierujesz go na prawdziwe archiwum.

Gotowy, by pożegnać panel „File Info”?

Dodatkowe zasoby