使用 Java 通过 Windows 上的外部应用程序管道数据
Posted
技术标签:
【中文标题】使用 Java 通过 Windows 上的外部应用程序管道数据【英文标题】:Piping data through an external application on Windows using Java 【发布时间】:2011-01-20 18:52:36 【问题描述】:我有一个带有 InputStream 的 Java 应用程序,它将数据复制到 OutputStream。我想使用 FreeArc 压缩来自 InputStream 的数据,然后再将其写入 OutputStream。
问题是没有针对 FreeArc 的 Java-API。因此,我需要以某种方式通过命令行 exe 对其进行管道传输。 IE。我必须欺骗 FreeArc,当它实际上是从我的 InputStream 读取并写入我的 OutputStream 时,它正在读取和写入两个文件。在 Unix 上这很简单,但我必须在 Windows 上进行。
你建议我怎么做?有没有办法在 Java 中访问 Windows 的命名管道,或者我可以通过套接字来访问?还有什么?这将完成 ~1/sec,因此开销不能太高。
【问题讨论】:
您需要哪种压缩方式? 我使用 FreeArc 是因为它的性能。我评估了 7-Zip 的 Java 版本,它太慢了。因此,我正在尝试这样做,看看它是否可以实现更好的性能。 您会使用不同的方法来压缩数据,还是除了 FreeArc 没有其他选择? 【参考方案1】:如果 freearc 可以从标准输入接收输入并将输出发送到标准输出,则可以使用ProcessBuilder
调用命令,返回的Process
实例将公开外部进程的标准输入和输出。
【讨论】:
谢谢,但不幸的是,它看起来不像 FreeArc 支持。【参考方案2】:这是一个示例,取自旨在通过 GhostScript 将 EPS 转换为 PDF 的代码:
// Start the script as OS process.
ProcessBuilder pb = new ProcessBuilder(gsExecutable, pdfFileName, epsFile.getName());
pb.directory(gsDir);
pb.redirectErrorStream(true);
Process proc = pb.start();
final InputStream stdErrInStream = proc.getErrorStream();
final InputStream stdOutInStream = proc.getInputStream();
// Read the STDERR-Stream.
String className = EpsToJpegConverter.class.getName();
final ByteArrayOutputStream stdErrOutStream = new ByteArrayOutputStream();
new Thread(new Runnable()
@Override
public void run()
try
byte[] buf = new byte[16];
int len = -1;
while ((len = stdErrInStream.read(buf)) != -1)
stdErrOutStream.write(buf, 0, len);
stdErrFertig = true;
catch (IOException e)
log.error(e.getLocalizedMessage(), e);
, className + " Script STDERR Reader").start();
// Read the STDOUT-Stream.
final ByteArrayOutputStream stdOutOutStream = new ByteArrayOutputStream();
new Thread(new Runnable()
@Override
public void run()
try
byte[] buf = new byte[4096];
int len = -1;
while ((len = stdOutInStream.read(buf)) != -1)
stdOutOutStream.write(buf, 0, len);
stdOutFertig = true;
catch (IOException e)
log.error(e.getLocalizedMessage(), e);
, className + " Script STDOUT Reader").start();
// Wait for the process to finish.
int waitFor = proc.waitFor();
if (waitFor != 0)
// If an error occured, the return code is != 0.
// In this case wait for the reading threads to finish.
while (!stdOutFertig || !stdErrFertig)
Thread.sleep(100);
throw new EpsConverterException("Das Konvertierungsscript " + gsExecutable
+ " wurde nicht erfolgreich ausgeführt.\nStandardausgabe:\n" + new String(stdOutOutStream.toByteArray())
+ "\nFehlerausgabe:\n" + new String(stdErrOutStream.toByteArray()));
HTH。
编辑: 也许读取转换后的图像字节的代码也很有趣:
// If everything worked out ok, read the PDF.
pdfFile = new File(gsDir, pdfFileName);
FileInputStream pdfInStream = new FileInputStream(pdfFile);
int len = -1;
byte[] buf = new byte[4096];
ByteArrayOutputStream pdfBAOS = new ByteArrayOutputStream(65535);
while ((len = pdfInStream.read(buf)) != -1)
pdfBAOS.write(buf, 0, len);
pdfInStream.close();
byte[] res = pdfBAOS.toByteArray();
return res;
【讨论】:
看起来您的代码假定输出已写入 STDOUT。我的问题是 FreeArc 不这样做。它写入文件。因此,我需要以某种方式重定向它。 以上代码的作用是为 GS 可执行文件提供两个文件名:EPS 输入和要写入 PDF 的路径。 STDOUT 和 STDERR 仅用于在 GS 失败时打印错误消息。如果 GS 成功,代码(未显示)随后读取 GS 生成的文件。 是的,那种显而易见的解决方案。我想在不写入文件系统的情况下解决这个问题。这就是我想重定向它的原因。 我不确定是否可以在两个进程之间以另一种方式传输数据,其中一个是 JVM,而 FreeArc 方面没有一些像样的 API。 也许您可以使用 JNI 将某些东西一起破解,并将 FreeArc 作为库嵌入,当您的程序打算独立于平台时,这可能会很痛苦。【参考方案3】:我建议使用作为 J2SE 库一部分的 ZIP 压缩(请参阅 java.util.zip
文档),除非使用 FreeArc 有巨大的性能优势(比如 1 秒与 10 秒),或者 ZIP 不能提供良好的足够的压缩。如果您热衷于使用 FreeArc,有几种不同复杂度的选项:
-
将输入转储到文件,使用
ProcessBuilder
对其运行实用程序,然后将压缩文件传输到输出。您应该使用 NIO 通道而不是流 - 这可能会稍微减少 IO 开销(尤其是发送出去)。这是您使用纯 Java 唯一可以做的事情,而且在性能方面显然不是最好的。
使用 JNI + 原生 FreeArc API(如果有的话,它允许对流/字节缓冲区进行操作)。
使用 JNI + 原生 Windows API 创建一个命名管道,将您的应用程序和 FreeArc 连接起来,然后您可以像使用常规文件一样使用它。
最后,如果输入或输出都是文件,则可以减少文件 IO。例如。如果您从文件中读取 - 只需直接对其运行 FreeArc,然后将压缩文件发送到输出。 此外,如果 a) 数据大小相对较小 - 可能高达几兆,则可能不值得这么麻烦;或 b) 压缩不会显着减少它
【讨论】:
以上是关于使用 Java 通过 Windows 上的外部应用程序管道数据的主要内容,如果未能解决你的问题,请参考以下文章
从 Android 上的 Qt 应用程序通过(显式)意图调用外部活动 - putExtra 不起作用
Windows 上的 Boost::process - 使用 MinGW?
如何判断 Linux 机器上的目录是不是在 Java 中安装了外部文件系统?
如何分析 Windows 和 Linux 上的应用程序以获取 CPU 和 RAM 使用值? [关闭]