💡 Повний робочий приклад доступний на GitHub:
remove-pii-from-office-metadata-java

Виклик відповідності: Чому ручний перегляд метаданих не працює у масштабі

Команда з управління записами надсилає 200 документів зовнішньому аудитору. Хтось прочитав кожну сторінку. Ніхто не переглянув властивості, а саме у властивостях містяться персональні дані: аналітик у Author, другий аналітик у LastSavedBy, керівник підрозділу у Manager, дочірня компанія у Company, мітка часу LastPrinted з нічого перед дедлайном і, у всьому, що проходило через SharePoint, ідентифікатор затверджувача та шлях робочого процесу.

Очищення метаданих — це робочий процес GroupDocs.Metadata для Java, який видаляє ці ідентифікаційні властивості з файлів Word, Excel та PowerPoint, а потім читає результат, щоб повідомити, що залишилося. У цій статті розглядається робочий процес, який би створила команда з відповідності: які групи властивостей існують, яке правило видалення підходить кожній, коли повне стирання замінює цільові проходи, і чому перевірка має бути в тому ж завданні, а не в чек‑лісті.

Проблема масштабу не в тому, що видалення складне. Проблема в тому, що ручний перегляд не створює запису. Аудитор, який запитує «які поля були видалені з цього файлу і коли», потребує цифр, а діалог властивостей їх не надає.

Чому загальні інструменти очищення тут не працюють

Діалог властивостей Windows редагує один файл за раз і охоплює лише підмножину полів. Інспектор документів Office працює інтерактивно, що виключає його з нічних завдань. Обидва залишають неторкнутими користувацькі частини OOXML і не записують нічого, що конвеєр міг би прочитати назад.

Команди, які спускаються на рівень нижче, редагуючи безпосередньо docProps/core.xml і docProps/custom.xml, беруть на себе навантаження з підтримки: XPath‑вираз для кожного поля, для кожного формату, який треба оновлювати щоразу, коли виробнича програма змінює назву. Така робота також помиляється у класифікації. Імена властивостей різняться між пакетами, тому список імен тихо застаріває, а правило, яке більше не підходить, виглядає так само, як файл, який уже чистий.

Рішення: GroupDocs.Metadata у робочому процесі управління записами

GroupDocs.Metadata для Java пропускає все через один пошуковий двигун властивостей. Об’єкт Specification визначає, які властивості підходять, removeProperties видаляє кожне підходження і повертає кількість змінених, а findProperties виконує той самий предикат лише для читання. Властивості несуть теги, тому Tags.getPerson().getCreator() ідентифікує поля типу автора незалежно від формату чи пакету, з якого вони походять.

У Java немає перевантаження лямбда‑виразу для removeProperties, що виявляється перевагою: кожне правило — це об’єкт, а об’єкти можна повторно використовувати. Той самий екземпляр специфікації, який очищає групу, можна передати скануванню перевірки, тому перевірка не відхилиться від очищення, яке вона має тестувати.

Реалізація крок за кроком конвеєра очищення

Крок 1 – Очистити групу ідентифікації за тегом

Чотири специфікації тегів, об’єднані за допомогою .or(...), охоплюють творця, редактора, менеджера та компанію. Інші поля не змінюються, тому Title, Subject і Keywords залишаються доступними для індексу записів.

try (Metadata metadata = new Metadata(inputPath)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
    int affected = metadata.removeProperties(
            new ContainsTagSpecification(Tags.getPerson().getCreator())
                .or(new ContainsTagSpecification(Tags.getPerson().getEditor()))
                .or(new ContainsTagSpecification(Tags.getPerson().getManager()))
                .or(new ContainsTagSpecification(Tags.getCorporate().getCompany())));
    metadata.save(outputPath);
    return affected;
}

Перевірка FileFormat.Unknown важливіша, ніж здається. Без неї нечитаємий файл повертає нуль видалень, що викликає плутанину між «файл був чистим при надходженні» і «файл не був прочитаний».

Крок 2 – Очистити сімейства полів за назвою

Ланцюжки коментарів, лічильники ревізій і серверні поля не мають тегу, тому їх підбирають за назвою. Малий підклас Specification приймає varargs‑список підрядків, що дозволяє одній класи використовувати три проходи:

public class NameContainsSpec extends Specification {
    private final String[] needles;

    public NameContainsSpec(String... needles) {
        this.needles = needles;
    }

    @Override
    public boolean isSatisfiedBy(MetadataProperty candidate) {
        String name = candidate.getName();
        if (name == null) return false;
        for (String n : needles) {
            if (name.contains(n)) return true;
        }
        return false;
    }
}

Таймлайн редагувань — це те, що найбільше цікавить команди з відповідності, бо кількість ревізій і дата останнього друку описують, як був створений документ:

try (Metadata metadata = new Metadata(inputPath)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
    int affected = metadata.removeProperties(new NameContainsSpec(
            "Revision", "TrackedChange", "LastPrinted", "TotalEditingTime", "EditTime"));
    metadata.save(outputPath);
    return affected;
}

Прохід коментарів і прохід SharePoint — це один і той же виклик з різними списками підрядків: Comment, Reviewer, Reviewed для слідів рецензій і Server, Workflow, Approver, ContentType, Template для полів серверу документів.

Крок 3 – Стирання на межі

Коли файл залишає організацію, селективність перестає мати сенс. Один виклик очищає кожен пакет метаданих, який виявляє бібліотека, включаючи користувацькі частини OOXML, які не охоплює жоден цільовий предикат:

try (Metadata metadata = new Metadata(inputPath)) {
    if (metadata.getFileFormat() == FileFormat.Unknown) return 0;
    int affected = metadata.sanitize();
    metadata.save(outputPath);
    return affected;
}

Крок 4 – Перевірка та класифікація залишків

Сканування виконує об’єднання всіх правил через findProperties і розподіляє результати по двох списках. Порожні значення та нульові лічильники пропускаються, а записи, назви яких починаються з Comment, Revision або Inspection, є обгортками над вмістом, а не над метаданими:

String value = "";
if (p.getValue() != null && p.getValue().getRawValue() != null) {
    value = String.valueOf(p.getValue().getRawValue());
}
if (value.isEmpty() || value.equals("0") || value.equals("0.0")) continue;
String entry = p.getName() + "=" + value;
String name = p.getName() == null ? "" : p.getName();
if (name.startsWith("Comment") || name.startsWith("Revision")
        || name.startsWith("Inspection")) {
    report.contentLevelLeaks.add(entry);
} else {
    report.metadataLeaks.add(entry);
}

Розподіл — це те, що зберігає сигнал «пройшов/не пройшов» чесним. Протікання метаданих має бути порожнім. Протікання на рівні вмісту залишаються лише інформативними, бо коментарі Word і автори відстежуваних змін живуть у word/document.xml, а бібліотека метаданих лише їх повідомляє без редагування; їх видалення потребує бібліотеки редагування вмісту, такої як Aspose.Words.

Коли цільовий прохід кращий за повне стирання?

Коли документ ще використовується. Файл, що циркулює між рецензентами, потребує Title, Subject і Keywords для пошуку та класифікації записів, а sanitize() видаляє їх усі. Запускайте проходи ідентифікації та коментарів під час співпраці, зберігайте описові поля і залишайте повне стирання на момент передачі файлу зовнішній стороні.

Реальний робочий процес: експортне завдання для зовнішнього аудитора

Уявіть нічне завдання. Воно читає список ідентифікаторів документів, копіює кожен файл у тимчасову папку, застосовує проходи ідентифікації та серверний прохід, викликає sanitize() для всього, що позначено як вихідне за межі організації, а потім виконує перевірку протікання над збереженою копією. Кожен крок додає свою кількість змінених до одного рядка журналу на файл, і непорожній список протікання метаданих призводить до провалу завдання, а не лише до попередження в журналі.

Колись я провів післяобідню сесію над версією цього завдання, яка повідомляла про нуль видалень у 40 файлах і виглядала як чистий батч. Вхідна папка містила застарілі бінарні .doc, захист формату повертався рано для кожного з них, і журнал не розрізняв «нічого не треба видаляти» і «нічого не прочитано». Додавання формату до запису разом із кількістю виправило проблему.

Бізнес‑вплив: що це змінює

Аспект Ручний перегляд Конвеєр GroupDocs.Metadata
Охоплення поля, видимі у діалоговому вікні властивостей кожен пакет, який виявляє бібліотека, включаючи користувацькі частини OOXML
Запис роботи нотатки, якщо хтось їх зробив кількість змінених на файл і на групу властивостей
Повторюваність залежить від того, хто виконував одна специфікація на правило, застосована ідентично до кожного файлу
Перевірка повторне відкриття файлу та перегляд сканування findProperties з двосторонньою класифікацією
Масштаб один файл за раз ті ж шість операцій у циклі по папці експорту

Інші сценарії, де підходить GroupDocs.Metadata

Той самий двигун властивостей читає так само добре, як і видаляє. Порівняння метаданих між двома версіями документа показує, що змінилося під час редагування, що корисно для спорів про власність і для виявлення файлу, який був переписаний поза процесом. Робота з metadata tags замість імен саме робить обидва випадки портативними між DOCX, XLSX, PPTX, PDF та форматами зображень.

Перші кроки з GroupDocs.Metadata для Java

Додайте репозиторій GroupDocs Java у pom.xml і підключіть com.groupdocs:groupdocs-metadata. Бібліотека працює в режимі оцінки без ліцензії, що достатньо для виконання всіх шести операцій над зразковим DOCX і перегляду підрахунків. Почніть з проходу ідентифікації, додайте проходи сімейства полів у міру потреби ваших джерел документів і підключіть перевірку протікання перед тим, як будь‑що потрапить у продакшн.

Ресурси