从java进程调用jpegoptim和pngquant,在stdin中传递图像,在stdout中读取结果:管道破裂或挂起



我在jpeg或png类型的变量中有一个imagen,我正在尝试通过java 1.6用命令行jpegoptim或pngquant转换它。

问题是我做不到。我读了很多答案,但没有找到答案。

这是我尝试的最后一个功能:

public int execute(byte[] image) throws IOException {
    ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
    final Process process = processBuilder.start();
    final ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
    final ByteArrayOutputStream errorBuffer = new ByteArrayOutputStream();
    try {
        // Handle stdout...
        new Thread() {
            public void run() {
                try {
                    IOUtils.copy(process.getInputStream(), outputBuffer);
                } catch (Exception e) {
                    LOG.error("Error executing " + commandLine,e);
                }
            }
        }.start();
        // Handle sderr...
        new Thread() {
            public void run() {
                try {
                    IOUtils.copy(process.getErrorStream(), errorBuffer);
                } catch (Exception e) {
                    LOG.error("Error executing " + commandLine,e);
                }
            }
        }.start();
        process.getOutputStream().write(image);
        process.getOutputStream().flush();
        process.waitFor();
        result = outputBuffer.toByteArray();
        errorMsg = errorBuffer.toString("ASCII");
    } catch (InterruptedException e) {
        LOG.error("Error executing " + commandLine,e);
    } finally {
        if( process != null ) {
            close(process.getErrorStream());
            close(process.getOutputStream());
            close(process.getInputStream());
            process.destroy();
        }
    }
    return process.exitValue();
}

命令行用于jpg:

this.commandLine = new ArrayList<String>();
this.commandLine.add("jpegoptim");
this.commandLine.add("-m90");
this.commandLine.add("--stdout");
this.commandLine.add("--stdin");

对于png:

this.commandLine = new ArrayList<String>();
this.commandLine.add("pngquant");
this.commandLine.add("--quality=70-80");
this.commandLine.add(">stdout");
this.commandLine.add("<stdin");

此代码的问题是管道破裂。我尝试了不同的编码方式。我在这里读过不同的论坛,但我不知道如何让它发挥作用。

提前谢谢。

编辑:

正如Linux弟子建议的那样,我将命令改为:

        List<String> commandLine = new ArrayList<String>();
        commandLine.add("tee");
        commandLine.add("-a"); 
        commandLine.add("logfile.log");

和代码:

    byte[] image = jpg.read(args[0]); // path to file...
    ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
    final Process process = processBuilder.start();
    OutputStream stdin = process.getOutputStream ();
    final InputStream stderr = process.getErrorStream ();
    final InputStream stdout = process.getInputStream ();
    Thread errorT = new Thread() {
        public void run() {
            byte[] buf = new byte[1024];
            int len;
            try {
                while ((len = stdout.read(buf)) != -1) {
                    // process byte buffer
                    System.out.write(buf, 0, len);
                    System.out.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };
    errorT.start();
    Thread outputT = new Thread() {
        public void run() {
            byte[] buf = new byte[1024];
            int len;
            try {
                while ((len = stderr.read(buf)) != -1) {
                    System.err.write(buf, 0, len);
                    System.err.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };
    outputT.start();
    stdin.write(image);
    stdin.flush();
    //stdin.close();
    try {
        process.waitFor();
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    stdin.close();
    stderr.close();
    stdout.close();

等待一去不复返。。。缺少什么?

如果我将"stdin.close();"放在flush之后(在上面的代码中有注释),则过程结束。但是stdout没有被完全处理,并且显示了一个错误:java.io.IOException:Stream关闭了两次(每个线程一次)。logfile.log等于映像,但stdout被截断。

我通过命令行测试:

java-cp testProc.jar testProc.Test image.jpg>image_new.jpg

logfile.log等于image.jpgimage_new.jpg较小,是image.jpg的截断版本。

有什么线索吗?

从最后两行开始。使用>stdin不能通过stdin。查看一些ProcessBuilder示例,了解如何使用BufferedWriter写入进程的stdin(Java进程的stdout),然后使用BufferedReader对子进程的stdout。如果在那之后仍然有问题,那么为了进行诊断,您可以尝试用tee -a logfile替换jpegoptimpngquant,这将获取stdin并直接将其管道传输回stdout(以及一个名为logfile的文件)
然后你得到了一个包含所有管道输入数据的文件,或者你会得到一个空文件和管道错误,因为你的Java程序没有传递数据,你可以看看它是否正确
如果您得到一个包含数据的文件,但没有得到管道错误,则可能意味着jpegoptim没有返回任何数据
此时,您可以在命令行上将数据从logfile管道传输到jpegoptim,并使用这些选项,直到它正常工作,然后在Java代码中使用这些工作选项。

终于成功了!

我完全靠运气找到了解决方案!。。。我打乱了这条线:

stdin.close();

并在"process.waitFor();"行之后添加:此行:

errorT.join();
outputT.join();

要处理图像,命令是:

commandLine.add("jpegoptim");
commandLine.add("-m90");
commandLine.add("--stdout");
commandLine.add("--stdin");

和:

commandLine.add("pngquant");
commandLine.add("--quality=70-80");
commandLine.add("-");

最终代码:

public int execute(List<String> commandLine, byte[] image) throws IOException {
    ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
    final Process process = processBuilder.start();
    OutputStream stdin = process.getOutputStream ();
    final InputStream stderr = process.getErrorStream ();
    final InputStream stdout = process.getInputStream ();

    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    Thread errorT = new Thread() {
        public void run() {
            byte[] buf = new byte[1024];
            int len;
            try {
                while ((len = stdout.read(buf)) != -1) {
                    // process byte buffer
                    bos.write(buf, 0, len);
                    bos.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };
    errorT.start();
    Thread outputT = new Thread() {
        public void run() {
            byte[] buf = new byte[1024];
            int len;
            try {
                while ((len = stderr.read(buf)) != -1) {
                    System.err.write(buf, 0, len);
                    System.err.flush();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };
    outputT.start();
    stdin.write(image);
    stdin.flush();
    stdin.close();
    try {
        process.waitFor();
        errorT.join();
        outputT.join();
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    stdin.close();
    stderr.close();
    stdout.close();

    System.out.write(bos.toByteArray());
    return 0;
}