使用iTextPDF移除PDF空白页致文件损坏,基于指定代码实现字节数组IO
解决iTextPDF(低版本)处理字节数组移除PDF空白页时文件损坏的问题
嘿,我看你在用旧版iText(就是com.lowagie.text那个包,也就是iText 2.x)处理字节数组输入输出、移除PDF空白页的时候碰到了文件损坏的问题。结合你提到的两个参考实现,我整理了修复后的完整代码,顺便给你讲讲容易踩坑的地方:
为啥会出现文件损坏?
大概率是这几个原因:
- 字节流没正确关闭或刷新,导致部分数据没写入输出流
Document和PdfCopy的生命周期没管好,比如没调用close()完成文档的最终写入- 读取PDF页面时没正确处理资源,导致写入新PDF时缺了必要的信息
修复后的完整代码
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.pdf.PdfCopy; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; public class RemoveBlankPageFromPDF { public static byte[] removeBlankPages(byte[] inputPdfBytes) throws IOException, DocumentException { // 把输入字节数组转成流,方便iText读取 ByteArrayInputStream inputStream = new ByteArrayInputStream(inputPdfBytes); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PdfReader reader = null; Document document = null; PdfCopy copy = null; try { reader = new PdfReader(inputStream); document = new Document(); // 用PdfCopy来复制非空白页面,比直接写更稳妥 copy = new PdfCopy(document, outputStream); document.open(); int totalPages = reader.getNumberOfPages(); for (int pageNum = 1; pageNum <= totalPages; pageNum++) { // 先判断当前页是不是空白页 if (!isBlankPage(reader, pageNum)) { PdfImportedPage page = copy.getImportedPage(reader, pageNum); copy.addPage(page); } } } finally { // 一定要按顺序关资源!先关document,它会触发copy完成写入 if (document != null && document.isOpen()) { document.close(); } // 再关reader,释放PDF资源 if (reader != null) { reader.close(); } inputStream.close(); } // 把处理后的流转成字节数组返回 return outputStream.toByteArray(); } // 参考你提到的实现,写的空白页判断逻辑,也可以根据需求调整 private static boolean isBlankPage(PdfReader reader, int pageNum) throws IOException { // 先拿裁剪框,没有的话用媒体框 com.lowagie.text.Rectangle pageRect = reader.getCropBox(pageNum); if (pageRect == null) { pageRect = reader.getMediaBox(pageNum); } // 简单判断:页面内容里有没有文本(BT标记)或者图形(BMC标记) // 如果需要更精确的判断,比如扫描像素,可以替换成你参考的像素扫描逻辑 String pageContent = reader.getPageContent(pageNum); return !pageContent.contains("BT") && !pageContent.contains("BMC"); } }
几个必须注意的点
- 资源关闭顺序:绝对不能乱!先关
Document,因为它会让PdfCopy把所有缓存的数据写入输出流;然后关PdfReader释放PDF资源;最后关输入流。顺序错了很容易导致文件不完整。 - 空白页判断逻辑:我这里用的是简单的标记判断,如果你需要更精准的(比如有些空白页可能有隐形内容),可以把
isBlankPage方法替换成你参考的两个实现里的像素扫描逻辑。 - 字节流处理:全程用
ByteArrayInputStream和ByteArrayOutputStream,不需要操作本地文件,完全基于内存处理字节数组,符合你的需求。 - 旧版iText的坑:你用的是
com.lowagie.text包,也就是iText 2.x,这个版本和后来的iText 7.x API完全不一样,要是以后升级版本,代码得大改。
怎么用?
举个简单的例子:
public static void main(String[] args) { try { // 假设inputBytes是你拿到的原始PDF字节数组 byte[] inputBytes = ...; // 调用方法移除空白页 byte[] processedPdfBytes = RemoveBlankPageFromPDF.removeBlankPages(inputBytes); // 接下来你就可以把processedPdfBytes存文件或者返回给调用方啦 } catch (IOException | DocumentException e) { e.printStackTrace(); } }
内容的提问来源于stack exchange,提问作者Juan Diego
相关产品推荐
相关产品推荐

