粮草机器人 Java操作PDF页面:删除单页、多页、空白页 工业级实现方案
结合你之前关注的Acl.Excel避坑、Apache PDFBox开源库使用、工业级文件处理稳定性的相关背景,
这套方案完全基于Apache PDFBox 2.0.x稳定版本实现,覆盖删除指定单页、批量删除多页、智能识
别删除空白页三类核心场景,同时补充了生产环境高频踩坑点,避免出现内存泄漏、PDF损坏等线上问题。
前置依赖配置
首先在Maven项目中引入PDFBox官方稳定依赖,避免使用版本过旧的快照版,防止出现已知的页面解析漏洞:
xml
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.33</version>
</dependency>
三类删除场景完整实现
1. 删除指定单页
直接调用PDDocument的removePage方法,注意PDFBox的页面下标从0开始计数,业务层传入的“第N页”
需要先减1再传入,避免出现删错页面的问题:
java
public void removeSinglePage(String inputPath, String outputPath, int targetPageNum) throws IOException {
// 使用try-with-resources自动关闭资源,避免内存泄漏
try (PDDocument document = PDDocument.load(new File(inputPath))) {
// 业务传入的页码从1开始,转换为PDFBox的0基下标
int pageIndex = targetPageNum - 1;
if (pageIndex < 0 || pageIndex >= document.getNumberOfPages()) {
throw new IllegalArgumentException("指定页码超出PDF总页数范围");
}
document.removePage(pageIndex);
document.save(outputPath);
}
}
2. 批量删除指定多页
先把需要删除的页码下标按从大到小排序,从后往前删除页面,避免从前往后删除时,页面下标发生偏移导致后续页面删错:
java
public void removeMultiPages(String inputPath, String outputPath, List<Integer> targetPageNums) throws IOException {
try (PDDocument document = PDDocument.load(new File(inputPath))) {
// 转换为0基下标,去重后按从大到小排序
List<Integer> indexList = targetPageNums.stream()
.map(num -> num - 1)
.filter(idx -> idx >= 0 && idx < document.getNumberOfPages())
.distinct()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
for (Integer idx : indexList) {
document.removePage(idx);
}
document.save(outputPath);
}
}
3. 智能识别删除空白页
不能只靠判断页面资源是否为空来识别空白页,要同时校验页面文本、绘制元素,避免把只有图片、只有线条的非空白页误判为空白页:
java
public void removeBlankPages(String inputPath, String outputPath) throws IOException {
try (PDDocument document = PDDocument.load(new File(inputPath))) {
int pageIdx = 0;
while (pageIdx < document.getNumberOfPages()) {
PDPage page = document.getPage(pageIdx);
if (isRealBlankPage(page)) {
document.removePage(pageIdx);
} else {
pageIdx++;
}
}
document.save(outputPath);
}
}
// 精准判断页面是否为真空白页
private boolean isRealBlankPage(PDPage page) throws IOException {
// 提取页面所有文本,文本长度大于0直接判定非空白
String pageText = new PDFTextStripper().getText(new PDDocument(Collections.singletonList(page)));
if (pageText.trim().length() > 0) {
return false;
}
// 校验页面资源中的图片、图形元素,存在元素直接判定非空白
if (page.getResources() == null) {
return true;
}
return page.getResources().getXObjects().isEmpty()
&& page.getResources().getFonts().isEmpty()
&& page.getResources().getPatterns().isEmpty();
}
生产环境避坑要点
所有PDDocument实例必须用try-with-resources包裹,自动关闭资源,避免大体积PDF处理后文件句柄泄漏,
导致后续无法再次读取文件。
处理加密PDF时,要先调用document.decrypt(密码)完成解密,再执行页面删除操作,否则会抛出加密权限异常。
大体积PDF处理时,开启MemoryUsageSetting.setupTempFileOnly()配置,把临时数据写入磁盘,避免OOM内存溢出。
处理完成后不要直接覆盖原文件,先写入临时文件,校验成功后再替换原文件,避免中途程序崩溃导致原PDF损坏。
需要我为你补充大体积PDF批量删除页面的内存优化版本代码,适配GB级大PDF处理场景,完全避免OOM问题吗?