PDFBox技术求助:PDF内图片非流式调整及转PNG遇OOM问题
解决方案:PDFBox非流式调整内部图片大小+优化PNG提取OOM问题
我来分享几个针对你问题的实用方案,既能搞定PDF内部图片的非流式缩放,又能优化OOM问题、减少用户抱怨:
一、无需流式处理调整PDF内部图片大小
要在非流式模式下修改PDF内的图片尺寸,核心思路是加载完整PDF后遍历页面资源,替换原图片为缩放后的版本,不需要重新构建内容流。以下是具体实现代码:
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.PDImageXObject; import org.apache.pdfbox.cos.COSName; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; public class PDFImageResizer { public static void resizePDFImages(String inputPath, String outputPath, float scaleFactor) throws IOException { try (PDDocument document = PDDocument.load(new File(inputPath))) { for (PDPage page : document.getPages()) { PDResources resources = page.getResources(); // 遍历页面所有XObject资源 for (COSName name : resources.getXObjectNames()) { PDXObject xobject = resources.getXObject(name); if (xobject instanceof PDImageXObject) { PDImageXObject originalImage = (PDImageXObject) xobject; BufferedImage bufferedImage = originalImage.getImage(); // 计算新尺寸 int newWidth = (int) (bufferedImage.getWidth() * scaleFactor); int newHeight = (int) (bufferedImage.getHeight() * scaleFactor); // 缩放图片 BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, bufferedImage.getType()); Graphics2D g2d = scaledImage.createGraphics(); // 开启抗锯齿,提升缩放后画质 g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null); g2d.dispose(); // 将缩放后的图片转为PDImageXObject,替换原资源 PDImageXObject newImage = PDImageXObject.createFromByteArray( document, imageToByteArray(scaledImage, "PNG"), name.getName() ); resources.put(name, newImage); } } } document.save(outputPath); } } // 辅助方法:将BufferedImage转为字节数组 private static byte[] imageToByteArray(BufferedImage image, String format) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, format, baos); return baos.toByteArray(); } }
注意事项:
- 这个方法会修改PDF内的图片资源本身,不会改变图片在页面上的布局位置,适合需要压缩PDF体积、降低图片分辨率的场景。
- 如果需要调整图片在页面上的显示大小(而非图片本身分辨率),则需要修改页面内容流的变换矩阵,但这属于流式处理范畴,不符合你的需求。
二、优化PNG提取的OOM问题,减少用户抱怨
你之前的固定阈值预检查太死板,容易误拒合理请求,导致用户不满。可以从以下几个维度优化:
1. 动态调整预检查阈值,结合系统可用内存
不要用固定的ALERT_IMAGE_SIZE_INSIDE_PDF,而是根据当前JVM的可用内存动态计算安全阈值,同时给用户提供“强制处理”的选项:
private boolean canProcessImage(PDImageXObject image) { // 计算图片渲染为PNG所需的内存(RGBA格式按4字节/像素计算) long requiredMemory = (long) image.getWidth() * image.getHeight() * 4; // 获取JVM当前可用内存,预留20%作为缓冲 long freeMemory = Runtime.getRuntime().freeMemory(); long safeThreshold = (long) (freeMemory * 0.8); // 优先判断是否在安全阈值内,否则询问用户是否强制处理 if (requiredMemory <= safeThreshold) { return true; } else { // 这里可以弹出用户提示,比如"当前图片过大,可能导致内存不足,是否继续?" return userConfirmsForceProcessing(); } }
2. 启用PDFBox内存优化选项
- 使用
FileResourceCache将临时数据缓存到磁盘,而非全部占用内存:
PDDocument document = PDDocument.load(new File("input.pdf")); // 设置磁盘缓存目录 document.setResourceCache(new org.apache.pdfbox.io.FileResourceCache(new File("temp-cache-dir")));
- 渲染时选择合适的图片类型,比如文档类PDF可以用灰度图(减少内存占用):
PDFRenderer renderer = new PDFRenderer(document); // 渲染为灰度图,内存占用仅为RGBA的1/4 BufferedImage image = renderer.renderImageWithDPI(pageIndex, 300, ImageType.GRAY);
3. 分块渲染超大图片
对于超出阈值的超大图片,不要一次性渲染,而是分块提取后拼接,降低单块内存占用:
public BufferedImage renderLargeImage(PDImageXObject image, int tileSize) throws IOException { int fullWidth = image.getWidth(); int fullHeight = image.getHeight(); BufferedImage result = new BufferedImage(fullWidth, fullHeight, image.getImage().getType()); Graphics2D g2d = result.createGraphics(); // 逐块渲染 for (int y = 0; y < fullHeight; y += tileSize) { for (int x = 0; x < fullWidth; x += tileSize) { int tileWidth = Math.min(tileSize, fullWidth - x); int tileHeight = Math.min(tileSize, fullHeight - y); // 提取当前区域的子图片 BufferedImage tile = image.getImage().getSubimage(x, y, tileWidth, tileHeight); g2d.drawImage(tile, x, y, null); } } g2d.dispose(); return result; }
通过以上方案,既能满足非流式调整PDF内部图片的需求,又能大幅降低OOM概率,同时通过动态阈值和用户选择权减少抱怨。
内容的提问来源于stack exchange,提问作者PcS
相关产品推荐
相关产品推荐

