读取其他进程的**无缓冲**输出流



我正在用java为文件转换器编程一个小GUI。文件转换器将其当前进度写入stdout。看起来像这样:

Flow_1.wav: 28% complete, ratio=0,447

我想在进度条中说明这一点,所以我正在阅读流程的stdout,如下所示:

ProcessBuilder builder = new ProcessBuilder("...");
builder.redirectErrorStream(true);
Process proc = builder.start();
InputStream stream = proc.getInputStream();
byte[] b = new byte[32];
int length;
while (true) {
    length = stream.read(b);
    if (length < 0) break;
    // processing data
}

现在的问题是,无论我选择哪个字节数组大小,流都是以4KB的块读取的。所以我的代码一直执行到length = stream.read(b);,然后阻塞了很长一段时间。一旦这个过程生成了4KB的输出数据,我的程序就会得到这个块,并以32字节的切片进行处理。然后再次等待下一个4KB。

我试图强迫java使用更小的缓冲区,比如

BufferedInputStream stream = new BufferedInputStream(proc.getInputStream(), 32);

或者这个:

BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()), 32);

但两者都没有改变。

然后我发现了这个:过程源(第87行附近)

Process类的实现方式似乎是将进程的stdout通过管道传输到一个文件。所以proc.getInputStream();实际做的是将流返回到文件。这个文件似乎是用4KB的缓冲区编写的。

有人知道这种情况的解决方法吗?我只想立即得到流程的输出。

EDIT:根据Ian Roberts的建议,我还尝试将转换器的输出通过管道传输到stderr流中,因为该流似乎没有封装在BufferedInputStream中。仍然是4k块。

另一件有趣的事情是:我实际上并没有得到4096个字节,但大约还有5个字节。恐怕FileInputStream本身是本地缓冲的。

查看链接到流程的标准输出流的代码时,会将其封装在BufferedInputStream中,但其标准error仍然没有缓冲区。因此,一种可能性可能不是直接执行转换器,而是执行一个shell脚本(如果您在Windows上,则为Windows等效脚本),将转换器的stdout发送到stderr:

ProcessBuilder builder = new ProcessBuilder("/bin/sh", "-c",
  "exec /path/to/converter args 1>&2");

不要redirectErrorStream,然后从proc.getErrorStream()而不是proc.getInputStream()读取。

可能是转换器已经在使用stderr进行进度报告,在这种情况下,您不需要脚本位,只需关闭redirectErrorStream()即可。如果转换器程序同时写入stdout和stderr,那么您还需要生成第二个线程来使用stdout(脚本方法通过将所有内容发送到stderr来绕过这一点)。

最新更新