繁体   English   中英

读取 PDF 时 InputStream 读取方法阻塞

[英]InputStream read method blocking while reading the PDF

我在 servlet 中使用以下代码在应用程序中读取和写入 PDF,但是在读取一些字节后,某些 PDF 的 read() 方法被阻止:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    InputStream is = null;
    OutputStream oos = null;
    try {
         String pdfPath = (String) request.getSession().getAttribute("viewPdfPath");
         
         File file=new File(pdfPath);
         
         oos = response.getOutputStream();
         response.setContentType("application/pdf");
         byte[] buf = new byte[8192];
    
         is= new FileInputStream(file);
         int c = 0;
         while ((c = is.read(buf, 0, buf.length)) > 0) { **//blocking after reading some bytes**
             oos.write(buf, 0, c);
             oos.flush();
         }
    
         oos.flush();
     } catch (FileNotFoundException e) {
            e.printStackTrace();
    }catch(Exception e){
        e.printStackTrace();
    } finally {
        if(oos != null)
            oos.close();
        if(is != null)
            is.close();
    }
}

The above code when executed from terminal as part of the standalone java class was successfully reading all bytes of the same PDF on the same Linux server where the application is currently hosted.

为什么 InputStream read() 方法作为应用程序的一部分被阻塞,但是当从同一个 Linux 服务器作为独立 java class 的一部分执行时,相同的代码没有阻塞?

根据JavadocFileInputStream::read是一个阻塞操作:

从此输入 stream 将最多 len 个字节的数据读取到字节数组中。 如果 len 不为零,则该方法将阻塞,直到某些输入可用

从同一台机器本地读取文件时,读取操作可能会很快完成,但这并不意味着理论上它不会阻塞等待操作系统、磁盘等几毫秒。从远程机器读取时,阻塞时间更有可能足以让您注意到。

看这里:

package com.example.mavenproject1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.IntStream;

public class ThreadTest {

  public static void main(String[] args) {
    // new thread pool executor, lets not be stingy: 24
    ExecutorService es = Executors.newFixedThreadPool(24);
    // ensure all threads complete:
    final ExecutorCompletionService<Void> cs = new ExecutorCompletionService<>(es);
    List<Future<Void>> futures = new ArrayList<>();
    // for i in 0..99:
    IntStream.range(0, 100).forEach(
        i -> {
          futures.add( // for later completion
              cs.submit( // start thread:
                  () -> {
                    try ( // try-with-res, also multiple possible!
                     // this "works like charm": 
                     InputStream is = ThreadTest.class.getResourceAsStream("/test.txt"); // 25 MB text file
                     // whereas this (seemingly) "never returns":
                     // InputStream is = new FileInputStream("C:\\Users\\Public\\Desktop\\test-out\\hello.txt"); // same file, but with new File(InputStream)!! Problem!!
                      // here: (I) write to different files (each thread), so no propblem:
                      FileOutputStream oos = new FileOutputStream("C:\\Users\\Public\\Desktop\\test-out\\hello" + i + ".txt")) {
                      byte[] buf = new byte[4096]; // a smaller buffer: 4Kib
                      int c;
                      while ((c = is.read(buf, 0, buf.length)) > 0) {
                        oos.write(buf, 0, c);
                      }
                      oos.flush(); // less flushing
                    } catch (IOException ex) {
                      Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    return null; // for the Future<Void>
                  }
              )
          );
        }
    );
    System.out.println("all threads submitted");
    futures.forEach( // collect all (fastest first)
        f -> {
          try {
            cs.take();
          } catch (InterruptedException ex) {
            Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
            Thread.currentThread().interrupt();
          }
        }
    );
    System.out.println("done");
    es.shutdown(); // <- otherwise: main hangs!
  }

}
  1. mkdir C:\Users\Public\Desktop\test-out\
  2. 运行此程序时(在名为test.txt的 class 路径中有一个多 MB 大小的文本文件,它会在 1-3 秒内完成(在我的机器上):
---------------------< com.example:mavenproject1 >----------------------
Building mavenproject1 1.0.0-SNAPSHOT
--------------------------------[ jar ]---------------------------------

--- exec-maven-plugin:3.0.0:exec (default-cli) @ mavenproject1 ---
all threads submitted
done
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time:  1.025 s
Finished at: 2022-01-08T18:00:52+01:00
------------------------------------------------------------------------

然而,当我们使用(使用相同/相似的文件)时:

InputStream is = new FileInputStream("C:\\Users\\Public\\Desktop\\test-out\\hello.txt")

...从文件(不是 class 路径 ressource.stream)中,程序最终陷入死锁:

---------------------< com.example:mavenproject1 >----------------------
Building mavenproject1 1.0.0-SNAPSHOT
--------------------------------[ jar ]---------------------------------

--- exec-maven-plugin:3.0.0:exec (default-cli) @ mavenproject1 ---
all threads submitted

结论:

new FileInputStream("someFile");

不是线程安全的(在同一个输入文件上),并且可以创建死锁。 #

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM