使用 iText 将 TIFF 转换为 PDF 的多线程方法

Multi threaded approach to convert TIFF to PDF using iText

我实际上是在尝试使用 itext 将 tiff 文件转换为 pdf,这非常简单。 但据我所知,TiffImage.getTiffImage 执行较大文件需要花费大量时间。

我的要求是使用FutureTaskExecutorService来提供多线程解决方案。这是我当前的代码:

import java.util.concurrent.Callable;

import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
import com.itextpdf.text.pdf.codec.TiffImage;

public class ProcessTiffImage implements Callable<Image>{

    RandomAccessFileOrArray tiffFile;
    int pageNo;

    public ProcessTiffImage(RandomAccessFileOrArray tiffFile, int pageNo){
        this.tiffFile = tiffFile;
        this.pageNo = pageNo;
    }

    public Image call() throws Exception {
        Image image = TiffImage.getTiffImage(tiffFile, pageNo);

        return image;

    }

}

转换方法是

public boolean convert(Document document) {

        int numOfThreads = Runtime.getRuntime().availableProcessors()  ;
        ExecutorService service = Executors.newFixedThreadPool(numOfThreads );
        List<FutureTask<Image>> taskList = new ArrayList<FutureTask<Image>>();
        List<Image> imageList = new ArrayList<Image>();

        for (int page = 1; page <= numOfPages; page++) {
            FutureTask<Image> futureTask = new FutureTask<Image>(new ProcessTiffImage(tiffFile, page));
            taskList.add(futureTask);
            service.execute(futureTask);
        }

        try {
            // Wait until all results are available
            for (FutureTask<Image> future : taskList) {

                imageList.add(future.get());
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } catch (ExecutionException ex) {
            ex.printStackTrace();
        }

        service.shutdown();


        boolean success = generatePdf(document, imageList);
        return success;
    }

但是我在 future.get() 得到了一个 NullPointerException。问题是执行没有等待 TiffImage.getTiffImage(tiffFile, pageNo) 完成。因此我无法创建图像列表。 非常感谢任何帮助。

堆栈跟踪

java.util.concurrent.ExecutionException: java.lang.NullPointerException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:188)
    at com.app.convertor.TiffParser.convert(TiffParser.java:107)
    at com.app.start.TiffToPdf.main(TiffToPdf.java:40)
Caused by: java.lang.NullPointerException
Caused by: java.lang.NullPointerException
    at com.itextpdf.text.pdf.codec.TIFFDirectory.getFieldAsLong(TIFFDirectory.java:467)
    at com.itextpdf.text.pdf.codec.TIFFDirectory.getFieldAsLong(TIFFDirectory.java:477)
    at com.itextpdf.text.pdf.codec.TiffImage.getTiffImage(TiffImage.java:124)
    at com.itextpdf.text.pdf.codec.TiffImage.getTiffImage(TiffImage.java:106)
    at com.app.processor.ProcessTiffImage.call(ProcessTiffImage.java:20)
    at com.app.processor.ProcessTiffImage.call(ProcessTiffImage.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

不要自己创建Future,请执行者为您创建,(即:提交Callable

List<Future<Image>> taskList = new ArrayList<Future<Image>>();

 for (int page = 1; page <= numOfPages; page++) {
     Future<Image> futureTask = service.submit(new ProcessTiffImage(tiffFile, page));
     taskList.add(futureTask);
 }

for (Future<Image> future : taskList) {
   imageList.add(future.get());
}

注意:请考虑改用 ExecutorCompletionService

这似乎是因为RandomAccessFileOrArray 不是线程安全的

您在 future.get() 调用中遇到的异常是 TiffImage.getTiffImage() 调用中的一个潜在异常,这似乎正在发生,因为该方法对它的 TIFF 数据不满意被通过。特别是,它正在寻找一些领域,但似乎没有。可能是图像高度或宽度?

但这是因为 RandomAccessFileOrArray 正在多个线程中使用。每当一个线程移动文件中的指针时,它就会搞砸其他线程正在做的事情。

您需要使用 RandomAccessFileOrArray.createView() 获取文件的新视图以传递给每个线程:

Image image = TiffImage.getTiffImage(tiffFile.createView(), pageNo);