Giới thiệu

Khi một phòng pháp chế nhận được nhiều file PDF hợp đồng, mỗi người xem thường thêm mật khẩu riêng để bảo vệ các điều khoản bí mật. Việc hợp nhất các file này thành một bìa duy nhất trở nên ác mộng vì mật khẩu khác nhau và các file không thể gộp trực tiếp. Giải mã thủ công tốn thời gian và dễ gây lỗi, đặc biệt khi phải xử lý hàng chục PDF.

Password‑merge là một quy trình làm việc của GroupDocs.Merger cho Java giúp mở khóa các PDF không đồng nhất và bảo vệ lại kết quả đã hợp nhất bằng một mật khẩu duy nhất. Hướng dẫn này sẽ chỉ cách phát hiện các PDF được bảo vệ, mở khóa chúng, hợp nhất nội dung và tùy chọn xoay mật khẩu thống nhất.

Bạn sẽ học cách cấu hình API Merger, xử lý luồng byte và tạo một PDF hợp nhất an toàn trong chưa đầy 30 dòng mã Java.

Khi nào nên hợp nhất các PDF được bảo vệ bằng mật khẩu?

Hợp nhất các PDF được bảo vệ bằng mật khẩu có ý nghĩa khi bạn cần một kho lưu trữ duy nhất, có thể tìm kiếm, tuân thủ các chính sách bảo mật đồng thời loại bỏ gánh nặng quản lý nhiều mật khẩu. Các kịch bản điển hình bao gồm các gói báo cáo tài chính hàng quý, các bìa hợp đồng cho kiểm toán, và các hồ sơ vụ án pháp lý nơi mỗi người đóng góp đã áp dụng mật khẩu khác nhau. Bằng cách mở khóa mỗi file một cách lập trình và áp dụng một mật khẩu thống nhất, bạn giữ cho kho lưu trữ an toàn và đơn giản hoá quy trình xem xét sau này. Toàn bộ thao tác có thể tự động hoá trong một pipeline CI, tiết kiệm hàng giờ công việc thủ công.

Điều kiện tiên quyết

  • Java 11 trở lên
  • GroupDocs.Merger for Java 24.6+ (temporary license)
  • Một tập hợp các file PDF, mỗi file có thể đi kèm mật khẩu tương ứng

Cài đặt thư viện qua Maven:

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

Bước 1 – Phát hiện PDF có được bảo vệ bằng mật khẩu hay không

Trước khi cố gắng mở khóa một file, hãy kiểm tra xem nó có thực sự có mật khẩu hay không. Điều này tránh việc xử lý không cần thiết và cho phép bạn ghi lại những file cần thông tin xác thực.

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

Các điểm chính:

  • LoadOptions chứa mật khẩu đã biết; nếu không cung cấp thì file sẽ được mở bình thường.
  • isPasswordSet() trả về true cho cả mật khẩu chủ sở hữu và người dùng.
  • Luôn luôn giải phóng đối tượng Merger bằng dispose() để giải phóng tài nguyên gốc.

Bước 2 – Mở khóa mỗi PDF và thu thập byte thô

Duyệt qua một map trong đó khóa là đường dẫn file và giá trị là mật khẩu của nó (null nếu không có). Phương thức trả về một danh sách các mảng byte đại diện cho các PDF đã được mở khóa.

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

Các điểm chính:

  • removePassword() loại bỏ bảo vệ hiện có.
  • Nội dung đã giải mã được ghi vào ByteArrayOutputStream để xử lý trong bộ nhớ.
  • Các file không có mật khẩu được đọc trực tiếp để giữ luồng xử lý đơn giản.

Bước 3 – Hợp nhất các PDF đã mở khóa và áp dụng mật khẩu thống nhất

Tạo một Merger từ luồng PDF đầu tiên, sau đó nối các luồng còn lại. Cuối cùng, bảo vệ tài liệu đã hợp nhất bằng 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);
}

Các điểm chính:

  • addPassword mã hoá PDF cuối cùng bằng thông tin xác thực được cung cấp.
  • Tất cả các thao tác diễn ra trong bộ nhớ; chỉ file cuối cùng mới được ghi ra đĩa.
  • Giải phóng Merger để giải phóng các handle gốc.

Bước 4 – Xoay mật khẩu trên một PDF đã được bảo vệ (tùy chọn)

Nếu tổ chức của bạn yêu cầu thay đổi mật khẩu định kỳ, bạn có thể cập nhật thông tin xác thực mà không cần hợp nhất lại các file nguồn.

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

Các điểm chính:

  • Tải PDF được bảo vệ bằng mật khẩu hiện tại.
  • updatePassword thay thế nó bằng thông tin xác thực mới.
  • Thao tác này nhanh vì không cần xử lý lại nội dung PDF.

Ứng dụng thực tế

Tôi đã gặp vấn đề này khi hợp nhất năm hợp đồng nhà đầu tư, mỗi hợp đồng có một mật khẩu reviewer khác nhau. Sử dụng các bước ở trên, tôi đã mở khóa tất cả các file, hợp nhất chúng thành một bìa duy nhất và áp dụng một mật khẩu thống nhất phù hợp với chính sách công ty. Toàn bộ quá trình chạy dưới hai phút trên một laptop tiêu chuẩn.

Các thực tiễn tốt nhất

  • Xác thực mật khẩu sớm: Dùng isDocumentProtected để đánh dấu các file có thể cần can thiệp thủ công.
  • Giới hạn việc sử dụng bộ nhớ: Đối với các PDF lớn, hãy stream chúng ra đĩa thay vì giữ toàn bộ mảng byte trong bộ nhớ.
  • Giải phóng đối tượng kịp thời: Lớp Merger giữ tài nguyên gốc; luôn gọi dispose() trong khối finally.
  • Chỉ sử dụng giấy phép tạm thời cho mục đích phát triển; mua giấy phép sản xuất trước khi phát hành.

Kết luận

GroupDocs.Merger for Java cung cấp một API sạch sẽ để mở khóa, hợp nhất và bảo mật lại các bộ sưu tập PDF. Bằng cách thực hiện bốn bước—phát hiện bảo vệ, mở khóa, hợp nhất với mật khẩu thống nhất và (tùy chọn) xoay mật khẩu—bạn có thể tự động hoá việc tạo các bìa PDF an toàn mà không cần can thiệp thủ công.

Các bước tiếp theo:

  • Khám phá các tùy chọn bổ sung như thiết lập siêu dữ liệu PDF sau khi hợp nhất (documentation).
  • Tìm hiểu cách hợp nhất PDF đồng thời giữ lại bookmark (API reference).
  • Xem dự án mẫu đầy đủ trên GitHub để có một triển khai sẵn sàng chạy.

Tài nguyên bổ sung