使用Apache PDFBox压缩PDF遇异常:文件损坏且体积增大
PDF压缩问题修复方案
问题根源
你的代码存在三个核心问题导致PDF损坏且体积增大:
- 输出逻辑错误:直接将处理后的图片字节写入输出流,没有修改原PDF文档的资源后保存完整文档,最终输出的是零散的图片数据而非合法PDF文件。
- 压缩策略不当:使用
LosslessFactory做无损压缩,对于本身已是有损压缩的JPG格式,这种操作不仅无法压缩,反而可能因重新编码增大体积。 - 依赖版本风险:使用PDFBox 3.0.0-RC1预览版,存在未修复的稳定性bug。
修复步骤
1. 更换为稳定版PDFBox依赖
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.32</version> <!-- 稳定版,兼容性与可靠性更强 --> </dependency>
2. 修正压缩逻辑
核心思路:遍历PDF内的图片资源,替换为压缩后的图片,最后保存完整的PDF文档,而非单独输出图片数据。针对不同图片格式采用适配策略:JPG用有损压缩调整质量,PNG尝试无损优化,其他格式保持原样。
修正后的完整代码:
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Iterator; public class PdfCompressor implements MediaCompressor { // JPG压缩质量(0.1-1.0,值越小体积越小,画质越低) private static final float JPG_COMPRESSION_QUALITY = 0.6f; @Override public byte[] compress(MediaData media) throws IOException { byte[] data = media.getData(); if (data == null || data.length == 0) { throw new IllegalArgumentException("Data must not be null or empty"); } try (PDDocument document = PDDocument.load(new ByteArrayInputStream(data)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { // 遍历所有页面的图片资源 for (PDPage page : document.getPages()) { PDResources resources = page.getResources(); if (resources == null) { continue; } Iterator<String> xObjectNames = resources.getXObjectNames().iterator(); while (xObjectNames.hasNext()) { String xObjectName = xObjectNames.next(); if (resources.isImageXObject(xObjectName)) { PDImageXObject imageXObject = (PDImageXObject) resources.getXObject(xObjectName); PDImageXObject compressedImage = compressImage(imageXObject, document); // 替换原页面中的图片资源 resources.put(xObjectName, compressedImage); } } } // 保存修改后的完整PDF文档 document.save(outputStream); return outputStream.toByteArray(); } } private PDImageXObject compressImage(PDImageXObject originalImage, PDDocument document) throws IOException { String format = originalImage.getSuffix(); if (format == null) { return originalImage; } BufferedImage bufferedImage = originalImage.getImage(); ByteArrayOutputStream tempStream = new ByteArrayOutputStream(); switch (format.toLowerCase()) { case "jpg": case "jpeg": // JPG采用有损压缩,调整质量参数控制体积 JPEGFactory.createFromImage(document, bufferedImage, JPG_COMPRESSION_QUALITY, tempStream); break; case "png": // PNG尝试无损优化 PDImageXObject losslessImage = LosslessFactory.createFromImage(document, bufferedImage); ImageIO.write(losslessImage.getImage(), "PNG", tempStream); break; default: // 不支持的格式直接返回原图片 return originalImage; } // 对比压缩前后体积,仅保留更小的版本 byte[] compressedData = tempStream.toByteArray(); if (compressedData.length < originalImage.getStream().getLength()) { return PDImageXObject.createFromByteArray(document, compressedData, originalImage.getFileName()); } else { return originalImage; } } }
额外注意事项
- AWS Lambda配置:PDF压缩需要一定内存支撑,建议将Lambda内存配置至少设为512MB,避免出现OOM异常。
- 大文件处理:若需处理超大PDF,建议采用分块处理或PDFBox流式API,减少内存占用。
- 测试覆盖:测试包含不同类型图片(JPG/PNG/BMP)、纯文本、矢量图形的PDF样本,确保压缩后文件可正常打开。
内容的提问来源于stack exchange,提问作者Chen Cohen
相关产品推荐
相关产品推荐

