【问题标题】:Copying InputStream to OutputStream halts unless I also write to System.out除非我也写入 System.out,否则将 InputStream 复制到 OutputStream 会停止
【发布时间】:2017-10-19 21:20:31
【问题描述】:

所以,我有一个从 Java 内部运行的 Python 进程。我正在尝试将其输出复制到 OutputStream。该过程正确运行;但是,每当我尝试将 Process#getInputStream()Process#getErrorStream() 复制到 OutputStream 时,程序就会挂起。
为了调试它,我添加了一个打印语句来在每次迭代时输出缓冲区,如下所示:

public static void copy(InputStream in, OutputStream out) throws IOException {

    byte[] buffer = new byte[4096];
    int n = 0;
    while ((n = in.read(buffer)) != -1) {
        // I have no clue why, but this only works if I print the output to sysdout
        System.out.println(new String(buffer));
        out.write(buffer, 0, n);
    }
}

出于某种奇怪的原因,这样做使一切都按预期工作。尝试刷新 OutputStream、刷新 System.out 或将空字符打印到 stdout,或打印纯 byte[] buffer 的事件不起作用,只有我上面的内容起作用。

发生这种情况的代码是怎么回事?

编辑:显示使用情况

public int runModule(OutputStream moduleOut, int argShowRange, List<String> arguments) throws IOException {

    int status = -1;
    Logger logger = Util.getOutputStreamLogger(moduleOut);
    logger.info("Starting module {}", getModuleName());

    ProcessBuilder exec = new ProcessBuilder();
    exec.directory(getWorkingDirectory());
    if (configureEnvironment(exec.environment(), moduleOut)) {

        List<String> command = getExecutable();
        command.addAll(arguments);
        exec.command(command);

        LOGGER.info("With PYTHONPATH: {}", exec.environment().get("PYTHONPATH"));
        LOGGER.info("In: {}", getWorkingDirectory());
        LOGGER.info("Executing: {}", StringUtils.join(command, " "));
        Process proc = exec.start();
        LOGGER.info("Copying input stream");
        copy(proc.getInputStream(), moduleOut);

        try {
            logger.info("Waiting for process");
            status = proc.waitFor();
            if (status != 0) {
                logger.error("The process failed with the following error: ");
                copy(proc.getErrorStream(), moduleOut);
            }
            logger.info("The process finished with exit code: {}", status);
        } catch (InterruptedException e) {
            LOGGER.warn("The thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    } else {
        logger.info("Module configuration failed");
    }
    Util.detachOutputStreamFromLogger(logger);

    return status;
} 

【问题讨论】:

  • 这不太可能。你同时修复了其他东西。注意应该是new String(buffer, 0, n),或者System.out.write(buffer, 0, n)。只要没有输入,复制循环就会阻塞。你是否关闭了他进程的输入流?你在消费它的标准错误流吗?
  • @EJP 我正在使用它来消耗标准错误流和标准输出流。从流中复制后,我调用Process#waitFor(),但执行从未达到该点。我将更新问题以显示我是如何使用它的。
  • 1.您还没有关闭进程的输入流。 2. 您需要合并输出流和错误流,或者在单独的线程中同时使用它们。 3.moduleOut连接到什么?
  • @EJP 连接到HttpServletResponse的输出流
  • 您只解决了三个编号点之一。你上次也做了同样的事情。我厌倦了重复自己。

标签: java inputstream outputstream


【解决方案1】:

正如 EJP 建议的那样,我通过执行器将流的复制拆分为单独的线程,然后确保在生成的进程完成后关闭它们:

public int runModule(OutputStream moduleOut, int argShowRange, List<String> arguments) throws IOException {

    int status = -1;
    Logger logger = Util.getOutputStreamLogger(moduleOut);
    logger.info("Starting module {}", getModuleName());

    ProcessBuilder exec = new ProcessBuilder();
    exec.directory(getWorkingDirectory());
    if (configureEnvironment(exec.environment(), moduleOut)) {

        List<String> command = getExecutable();
        command.addAll(arguments);
        exec.command(command);

        logger.info("With PYTHONPATH: {}", exec.environment().get("PYTHONPATH"));
        logger.info("In: {}", getWorkingDirectory());
        logger.info("Executing: {}", StringUtils.join(command, " "));
        Process proc = exec.start();

        try (InputStream procOut = proc.getInputStream(); InputStream procErrOut = proc.getErrorStream()) {
            copy(procOut, moduleOut);
            copy(procErrOut, moduleOut);
            LOGGER.debug("Waiting for process to finish");
            status = proc.waitFor();
            LOGGER.debug("Closing streams");
            procOut.close();
            procErrOut.close();
            logger.info("The process finished with exit code: {}", status);
        } catch (InterruptedException e) {
            LOGGER.warn("The thread was interrupted", e);
            Thread.currentThread().interrupt();
        }
    } else {
        logger.info("Module configuration failed");
    }
    Util.detachOutputStreamFromLogger(logger);

    return status;
}

public void copy(final InputStream in, final OutputStream out) throws IOException {
    executor.execute(() -> {
        try {
            byte[] buffer = new byte[4096];
            int n = 0;
            while ((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
        } catch (IOException e) {
            LOGGER.error("Ya done goofed", e);
        }
    });
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    相关资源
    最近更新 更多