首页 > 解决方案 > For 循环很慢 - Java

问题描述

我基本上想使用 Apache pdfbox 加载 pdf 并将其转换为每个页面的 base64 列表。

我尝试了以下代码,但它非常慢。我不需要转换成图像,我只想转换成base64传递给前端

PDDocument document = PDDocument.loadNonSeq(new File("Random.pdf"), null);
@SuppressWarnings("unchecked")
List<PDPage> pdPages = document.getDocumentCatalog().getAllPages();
int page = 0;
List<String> base64DocumentPages = new ArrayList<>();
for (PDPage pdPage : pdPages)
{ 
    ++page;           
    BufferedImage img = pdPage.convertToImage(BufferedImage.TYPE_INT_RGB, 300); // this is slow
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    ImageIOUtil.writeImage(img, ".png", os);
    String base64Page = Base64.getEncoder().encodeToString(os.toByteArray());
    base64DocumentPages.add(URLEncoder.encode(base64Page, "UTF-8"));
}
document.close();

我正在使用 PDFBOX 来循环页面,但如果您知道更多,我可以使用任何东西。

PS:我真的需要以某种数组分隔的页面的Base64数据

标签: javapdfbox

解决方案


你确定它的convertToImage方法?在我们的例子中,这是writeImage需要最长的方法。问题是您使用标准的 PNGWriter。这个有一个缺陷/错误,它总是在时间成本上使用最好的压缩。这已针对 Java 9 进行了修复,但在此之前有一个向后移植的版本可用。那么你需要做什么?

(1)将以下依赖项添加到您的maven projet(如果您不使用maven,则手动添加)

<dependency>
    <groupId>net.gredler</groupId>
    <artifactId>jdk9-png-writer-backport</artifactId>
    <version>1.0.0</version>
</dependency>

(2) 确保您使用 PdfBox 2.X

(3) 更改转换代码:

private static void convertMethod2(File pdf) {
        try (final PDDocument document = PDDocument.load(pdf)) {
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            List<String> base64DocumentPages = new ArrayList<>();               

            for (int page = 0; page < document.getNumberOfPages(); ++page) {
                BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150, ImageType.RGB);
                
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                PNGImageWriterBackport writer = chosePngWriter();
            
                if(writer!=null) {
                    try (ImageOutputStream stream = new MemoryCacheImageOutputStream(baos)) {
                        writer.setOutput(stream);
                        writer.write(null,new IIOImage(bim, null, null), getImageParams(writer));
                    }
                    finally {
                        writer.dispose();
                    }
                }
                else {
                    System.err.println("PNGImageWriterBackport not found! Aborting");
                }
                String base64Page = Base64.getEncoder().encodeToString(os.toByteArray());
                base64DocumentPages.add(URLEncoder.encode(base64Page, "UTF-8"));
            }
            document.close();
        }
        catch (IOException e) {
            //handle exception
        }
    }
    
    private static PNGImageWriterBackport chosePngWriter() {
        Iterator<ImageWriter> imageWriters = ImageIO.getImageWritersByFormatName("png");
        
        ImageWriter writer = null;
        while(imageWriters.hasNext()) {
        
            writer = imageWriters.next();
            if (writer instanceof PNGImageWriterBackport) {
                return (PNGImageWriterBackport)writer;
            }
        }
        return null;
    }
    
    private static ImageWriteParam getImageParams(PNGImageWriterBackport writer) {
        ImageWriteParam writeParam = writer.getDefaultWriteParam();
        //set compression mode which wasn't possible before
        writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        //0.0f highest compression, slowest
        //1.0f lowest compression, fastest
        writeParam.setCompressionQuality(0.86f);
        
        return writeParam;
    }

(4) 当然,如果可以将 DPI 降低到例如 150,它也会加快处理速度。但我知道这并不总是可能的......

对于最小增加 png 文件大小的价格,这将快得多......


推荐阅读