ProcessBuilderをのwaitFor外部アプリケーションを呼び出します

アプレットプロジェクトは当初、WAV形式にffmpegの変換マイクロチャネルのオーディオファイルを使用して、アリ雲のASR認識をテキストに。オーディオコンバータへのビデオは、最も一般的に使用さffmpegのです。

1
ffmpegの-i a.mp3 b.wav

関連記事:

質問は、Javaコールシステムffmpegのツールを使用する方法となります。Javaでは、プロセスのクラスをカプセル化するプロセスは、使用することができるRuntime.getRuntime().exec()、またはProcessBuilder新しいプロセス。

)(Runtime.getRuntime()。Execのから言えば

プロセスを開始する最も簡単な方法は、完全なコマンドに直接であるexec()パラメータ。

1 
2
3
4
5
6
7
{しようと
log.info("ピング10回");
Runtime.getRuntime()のexec(。"ピング10 127.0.0.1 -n");
log.info("完了")。
} キャッチ(IOExceptionを電子){
e.printStackTrace();
}

出力

1 
2
17:12:37.262 [メイン] INFO com.godzilla.Test -ピング10時間
17:12:37.272 [メイン] INFO com.godzilla.Test -完了

私は、コマンドの終了前に実行が行われるが、明らかではない印刷することを期待しています。

戻るには、子供のための待機をブロックするのwaitFor

プロセスクラスが提供waitFor方法。あなたは、呼び出し元のスレッドをブロックし、コードを返すことができます。0は通常の子プロセスを示しています。

1 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/ ** 
*必要に応じまで、現在のスレッドを待機させ
、この{で表される*プロセスた@codeプロセス}オブジェクト
*終了。サブプロセス場合、このメソッドはすぐに戻ります
*はすでに終了しています。サブプロセスがまだされていない場合
*終了し、呼び出し元のスレッドがするまでブロックされます
*サブプロセスが終了します。
*
* この表されるサブプロセスの終了値@return
* { @codeプロセス}オブジェクト。慣例により、値
* { @code 0}は正常終了を示しています。
* @throws InterruptedExceptionある現在のスレッドがある場合
* { 別によって@linkplainスレッド#割り込み()中断}
*スレッド、それは待っている間、その後、待機が終了して
* { @link InterruptedExceptionある}がスローされます。
* /
パブリック抽象int型のwaitFor ()例外:InterruptedExceptionがスローされます。
1 
2
3
4
5
6
7
8
9
10
11
12
{しようと
log.info("ピング10回");
プロセスp = Runtime.getRuntime()EXEC("ピング10 127.0.0.1 -n")。INTコード= p.waitFor()。IF(コード== 0){ log.info("完了")。} } キャッチ(IOExceptionを電子){ e.printStackTrace(); } キャッチ(InterruptedExceptionある電子){ e.printStackTrace(); }









出力

1 
2
17:15:28.557 [メイン] INFO com.godzilla.Test -ピング10時間
17:15:37.615 [メイン] INFO com.godzilla.Test -完了

ニーズを満たすように思われます。子プロセスが問題となっていた戻らない場合は、Javaプロセスがメインブロックされているだろうしかし、それから、これは非常に危険なものです。
この点で、java8はタイムアウトを待っている新しいインターフェイス、サポートを提供しています。int型ではない、インターフェイスの戻り値がブール値であることに注意してください。子プロセスが指定された時間内に終了すると、それはtrueを返します。

1
パブリックブールのwaitFor 長いタイムアウト、TimeUnitでユニット)

テストコード

1 
2
3
4
5
6
7
8
9
10
11
12
13
14
{しようと
log.info("ピング10回");
プロセスp = Runtime.getRuntime()EXEC("ピング10 127.0.0.1 -n")。ブール出口= p.waitFor(1、TimeUnit.SECONDS)。(出口){もしlog.info("完了")。} 他{ log.info("タイムアウト")。} } キャッチ(IOExceptionを電子){ e.printStackTrace(); } キャッチ(InterruptedExceptionある電子){ e.printStackTrace(); }











出力

1 
2
17:43:47.340 [メイン] INFO com.godzilla.Test -ピング10時間
17:43:48.352 [メイン] INFO com.godzilla.Test -タイムアウト

入力、出力、およびエラーストリームを取得します。

実行出力サブプロセスを取得するには、Processクラスを使用することができますgetInputStream()同様のがありますgetOutputStream()getErrorStream()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
log.info("ping");
Process p = Runtime.getRuntime().exec("ping -n 1 127.0.0.1");
p.waitFor();
BufferedReader bw = new BufferedReader(new InputStreamReader(p.getInputStream(), "GBK"));
String line = null;
while ((line = bw.readLine()) != null) {
System.out.println(line);
}
bw.close();
log.info("done")
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

注意,GBK是Windows平台的字符编码。
输出结果

1
2
3
4
5
6
7
8
9
10
18:28:21.396 [main] INFO com.godzilla.Test - ping

正在 Ping 127.0.0.1 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128

127.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 1,已接收 = 1,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 0ms,最长 = 0ms,平均 = 0ms
18:28:21.444 [main] INFO com.godzilla.Test - done

这里牵涉到一个技术细节,参考Process类的javadoc

1
2
3
4
5
6
7
8
9
10
11
12
* <p>By default, the created subprocess does not have its own terminal
* or console. All its standard I/O (i.e. stdin, stdout, stderr)
* operations will be redirected to the parent process, where they can
* be accessed via the streams obtained using the methods
* {@link #getOutputStream()},
* {@link #getInputStream()}, and
* {@link #getErrorStream()}.
* The parent process uses these streams to feed input to and get output
* from the subprocess. Because some native platforms only provide
* limited buffer size for standard input and output streams, failure
* to promptly write the input stream or read the output stream of
* the subprocess may cause the subprocess to block, or even deadlock.

翻译过来是,子进程默认没有自己的stdin、stdout、stderr,涉及这些流的操作,到会重定向到父进程。由于平台限制,可能导致缓冲区消耗完了,导致阻塞甚至死锁!

网上有的说法是,开启2个线程,分别读取子进程的stdout、stderr。
不过,既然说是By default,就是有非默认的方式,其实就是使用ProcessBuilder类,重定向流。此功能从java7开始支持。

ProcessBuilder和redirect

1
2
3
4
5
6
7
8
try {
log.info("ping");
Process p = new ProcessBuilder().command("ping -n 1 127.0.0.1").start();
p.waitFor();
log.info("done")
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}

输出结果

1
2
3
4
5
6
7
8
9
10
19:01:53.027 [main] INFO com.godzilla.Test - ping
java.io.IOException: Cannot run program "ping -n 1 127.0.0.1": CreateProcess error=2, 系统找不到指定的文件。
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at com.godzilla.Test.main(Test.java:13)
Caused by: java.io.IOException: CreateProcess error=2, 系统找不到指定的文件。
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(ProcessImpl.java:386)
at java.lang.ProcessImpl.start(ProcessImpl.java:137)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 1 more

此处有坑:ProcessBuilder的command列表要用字符串数组或者list形式传入! ps. 在小程序项目上,一开始把ffmpeg -i a.mp3 b.wav传入ProcessBuilder,却看不到生成的wav文件,查了日志CreateProcess error=2, 系统找不到指定的文件。还以为是ffmpeg路径问题。后来查了api才发现掉坑了。
正确的写法

1
Process p = new ProcessBuilder().command("ping", "-n", "1", "127.0.0.1").start();

刚才说的重定向问题,可以这样写

1
2
3
Process p = new ProcessBuilder().command("ping", "-n", "1", "127.0.0.1")
.redirectError(new File("stderr.txt"))
.start();

工作目录

默认子进程的工作目录继承于父进程。可以通过ProcessBuilder.directory()修改。

一些代码细节

ProcessBuilder.Redirect

java7增加了ProcessBuilder.Redirect抽象,实现子进程的流重定向。Redirect类有个Type枚举

1
2
3
4
5
6
7
public enum Type {
PIPE,
INHERIT,
READ,
WRITE,
APPEND
};

其中

  • PIPE: 表示子流程IO将通过管道连接到当前的Java进程。 这是子进程标准IO的默认处理。
  • INHERIT: 表示子进程IO源或目标将与当前进程的相同。 这是大多数操作系统命令解释器(shell)的正常行为。

对于不同类型的Redirect,覆盖下面的方法

  • append
  • appendTo
  • file
  • from
  • to

Runtime.exec()的实现

Runtime类的exec()底层也是用ProcessBuilder实现

1
2
3
4
5
6
7
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}

ProcessImpl

Process的底层实现类是ProcessImpl。
上面讲到流和Redirect,具体在ProcessImpl.start()方法

1
2
3
FileInputStreamのf0を= NULL; 
FileOutputStreamのF1 = NULL;
FileOutputStreamのF2 = NULL;

そして、もし面倒な山...他に判断Redirect.INHERIT、Redirect.PIPE、入力または出力ストリーム。

概要

  • プロセスのJavaクラスは、プロセスを抽象化したものです。ProcessImplは実装です。
  • Runtime.getRuntime()。Execの()とProcessBuilder.start()は子プロセスを開始することができます。Runtime.getRuntime()。Execの()は、基礎となる設定ProcessBuilderをあります
  • Runtime.getRuntime()。Execの()コマンドは、直接スペースを含む文字列全体を消費することができます。しかしProcessBuilder.command()は、配列またはリスト形式に文字列パラメータに渡さなければなりません
  • 子プロセスのデフォルトの実装と、親プロセスは非同期です。することができますProcess.waitFor()ブロックされて実現待ちます。
  • 標準エラー出力、デフォルトでは、子と親プロセスシェアSTDIN、STDOUT。ProcessBuilderをサポートリダイレクト対流(java7以降)
  • 通る流れをリダイレクトProcessBuilder.Redirect実装クラス。

おすすめ

転載: www.cnblogs.com/ycwu314/p/11291226.html