Введение

Когда юридический отдел получает несколько PDF‑договоров, каждый рецензент часто добавляет свой собственный пароль для защиты конфиденциальных пунктов. Консолидация этих файлов в один бандероль становится кошмаром, потому что пароли различаются и файлы нельзя объединить напрямую. Ручное снятие защиты занимает много времени и подвержено ошибкам, особенно при работе с десятками PDF‑файлов.

Password‑merge — это workflow GroupDocs.Merger для Java, который разблокирует разнородные PDF‑файлы и повторно защищает объединённый результат единой учётной записью. В этом руководстве рассматривается обнаружение защищённых PDF, их разблокировка, объединение содержимого и, при желании, ротация единого пароля.

Вы узнаете, как настроить Merger API, обрабатывать потоки байтов и создавать безопасный объединённый PDF менее чем в 30 строках кода Java.

Когда следует объединять PDF‑файлы, защищённые паролем?

Объединение PDF‑файлов, защищённых паролем, имеет смысл, когда вам нужен единый, индексируемый архив, который соблюдает политики безопасности, устраняя необходимость управлять множеством паролей. Типичные сценарии включают квартальные пакеты финансовых отчётов, бандероли договоров для аудита и юридические дела, где каждый участник применил свой пароль. Сняв защиту с каждого файла программно и применив единый пароль, вы сохраняете архив в безопасности и упрощаете последующие процессы проверки. Весь процесс можно автоматизировать в CI‑конвейере, экономя часы ручной работы.

Требования

  • Java 11 или новее
  • GroupDocs.Merger for Java 24.6+ (временная лицензия)
  • Набор PDF‑файлов, каждый из которых при необходимости имеет пароль

Установите библиотеку через Maven:

mvn dependency:copy -Dartifact=com.groupdocs:groupdocs-merger:24.6

Шаг 1 – Определение, защищён ли PDF паролем

Перед попыткой разблокировать файл проверьте, действительно ли он защищён паролем. Это избавит от ненужной обработки и позволит вам вести журнал файлов, требующих учётных данных.

// Returns true if the PDF at `path` has an owner or user password
public boolean isDocumentProtected(String path, String password) {
    Merger merger;
    if (password == null || password.isEmpty()) {
        merger = new Merger(path);
    } else {
        merger = new Merger(path, new LoadOptions(password));
    }
    try {
        return merger.isPasswordSet();
    } finally {
        merger.dispose();
    }
}

Ключевые моменты:

  • LoadOptions содержит известный пароль; если он не указан, файл открывается обычным способом.
  • isPasswordSet() возвращает true как для пароля владельца, так и для пароля пользователя.
  • Всегда освобождайте экземпляр Merger, вызывая dispose(), чтобы освободить нативные ресурсы.

Шаг 2 – Снятие защиты с каждого PDF и сбор необработанных байтов

Итерируйте по карте, где ключ — путь к файлу, а значение — его пароль (null, если пароля нет). Метод возвращает список массивов байтов, представляющих разблокированные PDF.

public List<byte[]> unlockAll(Map<String, String> sources) throws IOException {
    List<byte[]> unlocked = new ArrayList<>();
    for (Map.Entry<String, String> e : sources.entrySet()) {
        String path = e.getKey();
        String password = e.getValue();
        System.out.println("Unlocking (credentials=" +
                (password != null ? "yes" : "no") + "): " + path);
        if (password == null || password.isEmpty()) {
            unlocked.add(Files.readAllBytes(Paths.get(path)));
        } else {
            LoadOptions opts = new LoadOptions(password);
            ByteArrayOutputStream buf = new ByteArrayOutputStream();
            Merger m = new Merger(path, opts);
            try {
                m.removePassword();
                m.save(buf);
            } finally {
                m.dispose();
            }
            unlocked.add(buf.toByteArray());
        }
    }
    return unlocked;
}

Ключевые моменты:

  • removePassword() удаляет существующую защиту.
  • Расшифрованное содержимое записывается в ByteArrayOutputStream для обработки в памяти.
  • Файлы без пароля читаются напрямую, чтобы упростить процесс.

Шаг 3 – Объединение разблокированных PDF и применение единого пароля

Создайте Merger из первого потока PDF, затем присоедините остальные потоки. В конце защитите объединённый документ с помощью AddPasswordOptions.

public void mergeAndProtect(List<byte[]> unlockedPdfs,
                            String unifiedPassword,
                            String outputPath) {
    InputStream first = new ByteArrayInputStream(unlockedPdfs.get(0));
    Merger merger = new Merger(first);
    try {
        for (int i = 1; i < unlockedPdfs.size(); i++) {
            merger.join(new ByteArrayInputStream(unlockedPdfs.get(i)));
        }
        merger.addPassword(new AddPasswordOptions(unifiedPassword));
        merger.save(outputPath);
    } finally {
        merger.dispose();
    }
    System.out.println("Merged output: " + new File(outputPath).getAbsolutePath());
    System.out.println("Unified password: " + unifiedPassword);
}

Ключевые моменты:

  • addPassword шифрует итоговый PDF с указанными учётными данными.
  • Все операции происходят в памяти; только окончательный файл записывается на диск.
  • Освобождайте Merger, чтобы снять нативные дескрипторы.

Шаг 4 – Поворот пароля в уже защищённом PDF (необязательно)

Если ваша организация требует периодической ротации паролей, вы можете обновить учётные данные без повторного объединения исходных файлов.

public void rotateUnifiedPassword(String path,
                                 String oldPassword,
                                 String newPassword,
                                 String outputPath) {
    Merger merger = new Merger(path, new LoadOptions(oldPassword));
    try {
        merger.updatePassword(new UpdatePasswordOptions(newPassword));
        merger.save(outputPath);
    } finally {
        merger.dispose();
    }
    System.out.println("Rotated output: " + new File(outputPath).getAbsolutePath());
    System.out.println("New password: " + newPassword);
}

Ключевые моменты:

  • Загрузите защищённый PDF с текущим паролем.
  • updatePassword заменяет его новым учётным данным.
  • Операция быстрая, так как не требует повторной обработки содержимого PDF.

Применение в реальном мире

Я столкнулся с этим, когда консолидировал пять инвестиционных договоров, каждый из которых имел свой пароль рецензента. С помощью описанных шагов я разблокировал все файлы, объединил их в один бандероль и применил единый пароль, соответствующий нашей корпоративной политике. Весь процесс занял менее двух минут на обычном ноутбуке.

Лучшие практики

  • Validate passwords early: Use isDocumentProtected to flag files that may need manual attention.
  • Limit memory usage: For large PDFs, stream them to disk instead of keeping all byte arrays in memory.
  • Dispose objects promptly: The Merger class holds native resources; always call dispose() in a finally block.
  • Use a temporary license only for development; obtain a production license before release.

Заключение

GroupDocs.Merger for Java предоставляет чистый API для разблокировки, объединения и повторного обеспечения безопасности коллекций PDF. Следуя четырём шагам — определению защиты, разблокировке, объединению с единым паролем и, при необходимости, ротации пароля — вы можете автоматизировать создание защищённых PDF‑бандеролей без ручного вмешательства.

Следующие шаги:

  • Изучите дополнительные параметры, такие как установка метаданных PDF после объединения (документация).
  • Узнайте, как объединять PDF, сохраняя закладки (Ссылка на API).
  • Ознакомьтесь с полным примером проекта на GitHub для готовой к запуску реализации.

Дополнительные ресурсы