如何同时输出java.lang.Process的InputStream至System.out并重复读取?
解决Process输入流同时输出到System.out和供后续读取的方案
核心问题在于InputStream是一次性读取的,一旦数据被读取就会从流中移除,无法重复获取。要实现实时输出到System.out和保留输出供后续读取的双重目标,需要通过异步复制+缓存的方式处理原始流,同时包装Process对象来提供可重复读取的输入流。
实现步骤
- 自定义Process包装类:代理原始Process的所有方法,仅重写
getInputStream(),将原始流的数据异步复制到缓存和System.out,返回基于缓存的可重复读取流。 - 修改doSth方法:返回包装后的Process实例,而非原始Process。
完整代码示例
1. ProcessWrapper类(核心实现)
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ProcessWrapper implements Process { private final Process delegate; private final ByteArrayOutputStream outputCache = new ByteArrayOutputStream(); private boolean streamCopyCompleted; public ProcessWrapper(Process originalProcess) { this.delegate = originalProcess; // 启动后台线程异步复制流数据到缓存和System.out ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { try (InputStream originalIn = delegate.getInputStream()) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = originalIn.read(buffer)) != -1) { // 写入缓存 outputCache.write(buffer, 0, bytesRead); // 实时输出到System.out System.out.write(buffer, 0, bytesRead); System.out.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { streamCopyCompleted = true; executor.shutdown(); } }); } @Override public InputStream getInputStream() { // 返回基于缓存的输入流,每次调用生成新实例支持重复读取 return new ByteArrayInputStream(outputCache.toByteArray()); } // 代理Process接口的其他方法 @Override public OutputStream getOutputStream() { return delegate.getOutputStream(); } @Override public InputStream getErrorStream() { return delegate.getErrorStream(); } @Override public int waitFor() throws InterruptedException { int exitCode = delegate.waitFor(); // 等待流复制完成,确保后续读取能拿到完整输出 while (!streamCopyCompleted) { Thread.sleep(100); } return exitCode; } @Override public int exitValue() { return delegate.exitValue(); } @Override public void destroy() { delegate.destroy(); } }
2. 修改后的doSth方法
import java.io.File; import java.io.IOException; import java.util.List; public Process doSth(List<String> command, File workdir) throws IOException { Process originalProcess = new ProcessBuilder(command).directory(workdir).start(); // 返回包装后的Process实例 return new ProcessWrapper(originalProcess); }
关键说明
- 异步复制:通过后台线程处理原始流的读取,避免阻塞
doSth方法的返回,保证Process能正常返回供后续代码使用。 - 缓存机制:使用
ByteArrayOutputStream缓存所有输出数据,后续调用getInputStream()时返回ByteArrayInputStream,支持重复读取完整输出。 - 实时输出:后台线程每读取一段数据就立即写入
System.out,满足实时观察进程运行状态的需求。 - 线程安全:
ByteArrayOutputStream的write方法是同步的,缓存数据的写入和读取不会出现并发问题。
如果需要同时处理错误流(getErrorStream()),可以用同样的逻辑包装错误流,实现错误输出的实时打印和缓存读取。
内容的提问来源于stack exchange,提问作者uylmz
相关产品推荐
相关产品推荐

