2015-11-05 48 views
1

我試圖做一些看起來應該比較簡單的事情:從C#調用jpegoptim。用RedirectStandardInput和RedirectStandardOutput調用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

部分解決方案 - 請參閱下面的死鎖問題。我在原始嘗試中遇到了多個問題:

  1. 您需要構建一個將讀寫管道而不是文件的jpegoptim。如提到builds prior to mid-2014 can't do it。 jpegoptim的github「releases」是源代碼的無用拉鍊,沒有內置版本,因此您需要在別處查找實際的built releases

  2. 您需要正確地調用它,傳遞--stdin--stdout,取決於你怎麼會應對它,避免可能導致它寫什麼,就像-T1(這會,當優化是怎麼回事參數只有1%或更少,導致它不發出任何標準輸出)。

  3. 您需要執行的不平凡的任務:對工藝類

  4. 重定向輸入和輸出,並避免在輸入端的緩衝區溢出,將讓你0輸出再次 - 明顯stream.CopyToAsync()超出了Process的非常有限的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時會以這種方式運行(非常緩衝,非常類似於...),所以,此代碼可以很好地適應此特定任務。

相關問題