You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Apache PDFBox压缩PDF遇异常:文件损坏且体积增大

PDF压缩问题修复方案

问题根源

你的代码存在三个核心问题导致PDF损坏且体积增大:

  1. 输出逻辑错误:直接将处理后的图片字节写入输出流,没有修改原PDF文档的资源后保存完整文档,最终输出的是零散的图片数据而非合法PDF文件。
  2. 压缩策略不当:使用LosslessFactory做无损压缩,对于本身已是有损压缩的JPG格式,这种操作不仅无法压缩,反而可能因重新编码增大体积。
  3. 依赖版本风险:使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 10:10:26