我正在尝试做一些看起来应该相对简单的事情:从C#调用jpegoptim。
我可以让它写入磁盘很好,但是让它接受一个流并发出一个流到目前为止还没有找到我 - 我总是以0长度输出或不祥的“管道已经结束。”
我试过的一种方法:
var processInfo = new ProcessInfo(
jpegOptimPath,
"-m" + quality + " -T1 -o -p --strip-all --all-normal"
);
processInfo.CreateNoWindow = true;
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardInput = true;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
using(var process = Process.Start(processInfo))
{
await Task.WhenAll(
inputStream.CopyToAsync(process.StandardInput.BaseStream),
process.StandardOutput.BaseStream.CopyToAsync(outputStream)
);
while (!process.HasExited)
{
await Task.Delay(100);
}
// Do stuff with outputStream here - always length 0 or exception
}
我也试过这个解决方案:
http://alabaxblog.info/2013/06/redirectstandardoutput-beginoutputreadline-pattern-broken/
using (var process = new Process())
{
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = fileName;
process.StartInfo.Arguments = arguments;
process.Start();
//Thread.Sleep(100);
using (Task processWaiter = Task.Factory.StartNew(() => process.WaitForExit()))
using (Task<string> outputReader = Task.Factory.StartNew(() => process.StandardOutput.ReadToEnd()))
using (Task<string> errorReader = Task.Factory.StartNew(() => process.StandardError.ReadToEnd()))
{
Task.WaitAll(processWaiter, outputReader, errorReader);
standardOutput = outputReader.Result;
standardError = errorReader.Result;
}
}
同样的问题。输出长度为0.如果我让jpegoptim在没有输出重定向的情况下运行,我会得到我所期望的 - 一个优化的文件 - 但是当我以这种方式运行它时不会。
有必要这样做吗?
更新:发现一条线索 - 我不觉得羞怯 - jpegoptim从未支持到stdin的管道直到2014年的实验性构建,今年已修复。我的版本来自2013年的旧图书馆。https://github.com/tjko/jpegoptim/issues/6
答案 0 :(得分:0)
部分解决方案 - 请参阅下面的死锁问题。我原来的尝试中遇到了多个问题:
您需要构建jpegoptim,它将读取和写入管道而不是仅限文件。如上所述builds prior to mid-2014 can't do it。 jpegoptim的github“releases”是源代码的无用拉链,而不是内置的版本,因此您需要在其他地方查找实际的built releases。
您需要正确调用它,传递--stdin
和--stdout
,并根据您对它的响应方式,避免可能导致其无法写入的参数,例如 - T1(当优化仅为1%或更低时,将导致它不向stdout发射任何东西)。
您需要执行以下重要任务:在Process类上重定向输入和输出
并避免输入端的缓冲区溢出,再次使您获得0输出 - 明显的stream.CopyToAsync()溢出进程的非常有限的4096字节(4K)缓冲区并且什么也得不到。
这么多路线都没有。没有说明原因。
var processInfo = new ProcessInfo(
jpegOptimPath,
"-m" + quality + " --strip-all --all-normal --stdin --stdout",
);
processInfo.CreateNoWindow = true;
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardInput = true;
processInfo.RedirectStandardOutput = true;
processInfo.RedirectStandardError = true;
using(var process = new Process())
{
process.StartInfo = processInfo;
process.Start();
int chunkSize = 4096; // Process has a limited 4096 byte buffer
var buffer = new byte[chunkSize];
int bufferLen = 0;
var inputStream = process.StandardInput.BaseStream;
var outputStream = process.StandardOutput.BaseStream;
do
{
bufferLen = await input.ReadAsync(buffer, 0, chunkSize);
await inputStream.WriteAsync(buffer, 0, bufferLen);
inputStream.Flush();
}
while (bufferLen == chunkSize);
do
{
bufferLen = await outputStream.ReadAsync(buffer, 0, chunkSize);
if (bufferLen > 0)
await output.WriteAsync(buffer, 0, bufferLen);
}
while (bufferLen > 0);
while(!process.HasExited)
{
await Task.Delay(100);
}
output.Flush();
这里有一些需要改进的地方。欢迎改进。
最大问题:在某些图片上,outputStream.ReadAsync行发生此死锁。
这一切都属于单独的方法来分解它 - 我展开了一堆方法来保持这个例子的简单。
有许多可能没有必要的冲洗。
此处的代码旨在处理流入和流出的任何内容。 4096是任何进程都会处理的硬限制,但假设所有输入进入,然后输出的所有输出都可能是坏的,并且基于我的研究可能导致其他类型进程的死锁。似乎jpegoptim在传递--stdin --stdout时表现为这种(非常缓冲,非常类似...)的方式,但是,这个代码很好地应对了这个特定的任务。