当前位置: 首页 > 知识库问答 >
问题:

无法使用具有多个线程的PDFTextStripper读取单个页面

仉伟兆
2023-03-14

我可以创建10个线程。但问题是,当我试图以并行方式使用这些线程访问单个页面时。我也尝试过将私有静态PDFTextStripper实例放入同步块中。但我还是得到了以下例外:

COSStream已被关闭,无法读取。也许它的附件已经被关闭?

试图打印前10页每页的第一个单词,但不起作用。这是我第一次尝试多线程和PDF阅读。任何帮助都将不胜感激。

public class ReadPDFFile extends Thread implements FileInstance {
    private static String fileLocation;
    private static String fileNameIV;
    private static String userInput;
    private static int userConfidence;
    private static int totalPages;
    private static ConcurrentHashMap<Integer, List<String>> map = null;
    private Iterator<PDDocument> iteratorForThisDoc;
    private PDFTextStripperByArea text;
    private static PDFTextStripper pdfStrip = null;
    private static PDFParser pdParser = null;
    private Splitter splitter;
    private static int counter=0;
    private StringWriter writer;
    private static  ReentrantLock counterLock = new ReentrantLock(true);
    private static PDDocument doc;
    private static PDDocument doc2;
    private static boolean flag = false;
    List<PDDocument> listOfPages;

    ReadPDFFile(String filePath, String fileName, String userSearch, int confidence) throws FileNotFoundException{
        fileLocation= filePath;
        fileNameIV =  fileName;
        userInput= userSearch;
        userConfidence = confidence;
        System.out.println("object created");
    }

    @Override
    public void createFileInstance(String filePath, String fileName) {
        List<String> list = new ArrayList<String>();
        map = new ConcurrentHashMap<Integer, List<String>>();
        try(PDDocument document = PDDocument.load(new File(filePath))){
            doc = document;
            pdfStrip = new PDFTextStripper();
            this.splitter = new Splitter();
            text = new PDFTextStripperByArea();
            document.getClass();
            if(!document.isEncrypted()) {
                totalPages = document.getNumberOfPages();
                System.out.println("Number of pages in this book "+totalPages);
                listOfPages = splitter.split(document);
                iteratorForThisDoc = listOfPages.iterator();
            }
            this.createThreads();
            /*
             * for(int i=0;i<1759;i++) { readThisPage(i, pdfStrip); } flag= true;
             */
        }
        catch(IOException ie) {
            ie.printStackTrace();
        }
    }

    public void createThreads() {
        counter=1;
        for(int i=0;i<=9;i++) {
            ReadPDFFile pdf = new ReadPDFFile();
            pdf.setName("Reader"+i);
            pdf.start();
        }
    }

    public void run() {
        try {
            while(counter < 10){
                int pgNum= pageCounterReentrant();
                readThisPage(pgNum, pdfStrip);
            }
            doc.close();
        }catch(Exception e) {
        }   
        flag= true;
    }

    public static int getCounter() {
        counter=  counter+1;
        return counter;
    }

    public static int pageCounterReentrant() {
        counterLock.lock();
        try {
            counter =  getCounter();
        } finally {
            counterLock.unlock();
        }
        return counter;
    }

    public static void readThisPage(int pageNum, PDFTextStripper ts) {
        counter++;
        System.out.println(Thread.currentThread().getName()+" reading page: "+pageNum+", counter: "+counter);

        synchronized(ts){
            String currentpageContent= new String();
            try {
                ts.setStartPage(pageNum);
                ts.setEndPage(pageNum);
                System.out.println("-->"+ts.getPageEnd());
                currentpageContent = ts.getText(doc);
                currentpageContent = currentpageContent.substring(0, 10);
                System.out.println("\n\n "+currentpageContent);
            }

        /*
         * further operations on currentpageContent here
         */

            catch(IOException io) {
                io.printStackTrace();
            }finally {
            }
        } 
    }

    public static void printFinalResult(ConcurrentHashMap<Integer, List<String>> map) {
        /*
         * simply display content of ConcurrentHashMap
         */
    }

    public static void main(String[] args) throws FileNotFoundException {
        Scanner sc = new Scanner(System.in); 
        System.out.println("Search Word");
        userInput = sc.nextLine(); 
        System.out.println("Confidence"); 
        userConfidence = sc.nextInt(); 
        ReadPDFFile pef = new ReadPDFFile("file path", "file name",userInput, userConfidence);
        pef.createFileInstance("file path ","file name");
        if(flag==true)
            printFinalResult(map);
     }
}

如果我使用一个线程按顺序读取for循环中的每个页面,那么它可以打印内容,但不能使用多线程。在这之后,可以看到代码在void createFileInstance()中被注释。创建线程();我希望使用线程分别获取每个pdf页面的字符串内容,然后对其执行操作。我有逻辑将每个单词标记收集到列表中,但在继续之前,我需要解决这个问题。

共有1个答案

聂华翰
2023-03-14

您的代码如下所示:

try(PDDocument document = PDDocument.load(new File(filePath))){
    doc = document;
    ....
    this.createThreads();
} // document gets closed here
...
//threads that do text extraction still running here (and using a closed object)

这些线程使用doc来提取文本(ts.getText(doc))。但此时,由于使用了try with resources,PDDocument对象已经关闭,其流也已关闭。因此,会出现错误消息“可能其附带的PDDocument已关闭?”。

您应该在关闭文档之前创建线程,并在关闭文档之前等待所有线程完成。

我建议不要在一个PDF文档上使用多线程,请参阅PDFBOX-4559。你可以创建几个PDC文档,然后在这些文档上提取,或者根本不做。文本提取在PDFBox中工作得非常快(与渲染相比)。

 类似资料:
  • 问题内容: 在Java中拥有多个线程池的优缺点是什么?我已经看过代码,其中有多个线程池用于不同的“类型”任务,而且我不确定它是更好的设计还是只是开发人员感到懒惰。一个示例是将ScheduledThreadPoolExecutor用于定期执行的任务或具有超时的任务,而将另一ThreadPoolExecutor用于其他任务。 问题答案: 具有单独的专用线程池的目的是,使活动不会因线程不足而被饥饿,因为

  • 我想使用amqp设置一个消费者,以便从特定队列读取。一些谷歌指出,这可以通过amqp_basic_get来实现,查看文档,实际的消息是通过amqp_read_消息检索的。我还发现了这个例子,我试图按照这个例子来实现基本的。然而,我无法从特定队列获取和读取消息。 我的场景是这样的:我有两个程序,通过发布和使用Rabbitmq服务器进行通信。在每个通道中,都声明了一个连接,有两个通道,一个用于消费,一

  • 问题内容: 我有一个类,其中每30秒从一个后台线程填充一个映射,然后有一个方法,该方法将由多个读取器线程调用以获取可用的实时套接字,该套接字使用相同的映射来获取此信息。 如您在以上课程中所见: 从每30秒运行一次的单个后台线程,我使用所有活动套接字填充map。 然后,从多个线程中,我调用方法给我一个可用的实时套接字,该套接字使用map获取所需的信息。 我上面的代码线程安全吗,所有阅读器线程都可以正

  • 我正在开发基于spring+Hibernate的web应用程序。在这个应用程序中,我必须对数据库中的50000个可用记录进行计算。当前逻辑:- 循环0到50000(所有50000记录彼此独立) 选择第i个元素 对第i个元素执行计算(删除CALCULATION_TEMP表(如果存在),创建新表CALCULATION_TEMP并在CALCULATION_TEMP表中插入计算) 在步骤3表上进行一些计算

  • 问题内容: 我需要一些帮助,以使用同一持久性单元配置多个数据库的多个连接。 它们都具有相同的架构。因此,我想使用相同的持久性单元/ DAO等,而又不想设置10个EntityManager,10个持久性xml等。有没有办法做到这一点?这是我当前的配置: 我也在使用Spring / hibernate来设置我的上下文: 最后我使用: 将我的EntityManager注入我的DAO 如何扩展此模型以能够

  • 问题内容: 我已经在一个类的单个方法中初始化了InputStream,并将其传递给下一个方法进行处理。InputStream本质上封装了CSV文件以进行处理。 另一个方法调用传入同一个InputStream的2个不同方法,一个用于检索标头,另一个用于处理内容。该结构如下所示: 我在这里做错什么了吗?有什么方法可以在不同的方法调用之间重用InputStream。 我正在提出可以模仿以下问题的完整程序