介绍
当法务部门收到多份合同 PDF 时,每位审阅者通常会为保密条款设置自己的密码。将这些文件合并成一个文件夹变得异常困难,因为密码各不相同,文件无法直接合并。手动解密既耗时又容易出错,尤其是面对数十个 PDF 时更是如此。
Password‑merge 是 GroupDocs.Merger 的 Java 工作流,可解锁不同密码的 PDF 并使用单一凭证重新保护合并后的结果。本教程将演示如何检测受保护的 PDF、解锁它们、合并内容,以及(可选)对统一密码进行轮换。
您将学习如何配置 Merger API、处理字节流,并在不到 30 行 Java 代码的情况下生成安全的合并 PDF。
何时应合并受密码保护的 PDF?
只要需要一个既安全又可搜索的归档,同时希望消除管理多个密码的负担,就可以合并受密码保护的 PDF。典型场景包括季度财务报告合集、审计用的合同文件夹以及每位贡献者都设置了不同密码的法律案件文件。通过以编程方式解锁每个文件并使用统一密码重新加密,既能保持档案安全,又能简化后续审阅流程。整个过程可以在 CI 流水线中自动化,节省大量人工工作时间。
前提条件
- Java 11 或更高版本
- GroupDocs.Merger for Java 24.6+(temporary license)
- 一组 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。- 必须在
finally块中调用dispose()以释放本机资源。
步骤 2 – 解锁每个 PDF 并收集原始字节
遍历一个映射,其中键为文件路径,值为对应的密码(若无则为 null)。该方法返回一个 byte[] 列表,表示已解锁的 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 并应用统一密码
从第一个 PDF 流创建 Merger,随后加入其余流。最后使用 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 进行加密。- 所有操作均在内存中完成,仅在最后将文件写入磁盘。
- 同样需要在
finally中调用dispose()释放本机句柄。
步骤 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 内容。
实际应用案例
我在合并五份投资者合同时遇到了此问题,每份合同都有不同审阅者设置的密码。按照上述步骤,我解锁了所有文件,合并成一个文件夹,并使用符合公司政策的单一密码进行保护。整个过程在普通笔记本电脑上不到两分钟即可完成。
最佳实践
- 提前验证密码:使用
isDocumentProtected标记可能需要人工介入的文件。 - 限制内存使用:对于大型 PDF,建议将其流式写入磁盘,而不是全部保存在内存中。
- 及时释放对象:Merger 类持有本机资源,务必在
finally块中调用dispose()。 - 仅在开发阶段使用临时许可证;发布前请获取正式许可证。
结论
GroupDocs.Merger for Java 提供了简洁的 API 来解锁、合并并重新保护 PDF 集合。通过四个步骤——检测保护、解锁、使用统一密码合并以及(可选)轮换密码——您可以实现安全 PDF 绑定的全自动化,无需人工干预。
后续步骤:
- 探索更多选项,例如在合并后设置 PDF 元数据(documentation)。
- 学习如何在保留书签的情况下合并 PDF(API reference)。
- 查看 GitHub 上的完整示例项目,获取可直接运行的实现。