You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何同时输出java.lang.Process的InputStream至System.out并重复读取?

解决Process输入流同时输出到System.out和供后续读取的方案

核心问题在于InputStream是一次性读取的,一旦数据被读取就会从流中移除,无法重复获取。要实现实时输出到System.out和保留输出供后续读取的双重目标,需要通过异步复制+缓存的方式处理原始流,同时包装Process对象来提供可重复读取的输入流。

实现步骤

  1. 自定义Process包装类:代理原始Process的所有方法,仅重写getInputStream(),将原始流的数据异步复制到缓存和System.out,返回基于缓存的可重复读取流。
  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:17:31