我正在开发一个网站控件,用户可以上传他的PDF,上传后第三方CLI工具启动以根据特定的配置文件验证PDF并生成相应的报告。 提到的工具是callas pdfToolbox 4(可在此处http://www.callassoftware.com/callas/doku.php/en:download[ ^])
问题在于我在网站上的控制权,我需要在检查PDF文件的实时进度条中显示。这个功能的所有AJAX东西都已经写好了(ajax-postbacks,进度条更新等),但是这个进程的异步更新存在问题,启动了pdf检查工具。
如果从命令行窗口启动该工具,您可以看到它生成标准输出流的输出,其中包含进度更新(百分比),以及有关PDF文件中错误的可能消息。 / p>
但是,如果该工具是由我在web控件中创建的进程启动的,那么在检查完成之前我不会收到OutputDataReceived事件,然后一次又一次地发出许多OutputDataReceived事件。
我的代码如下(我编写了一个小型控制台应用程序来更快地测试):
class Program
{
static string appString = "path-to-callas-cli";
static string argString = "path-to-pdf-and-path-to-report-and-path-to-callas-profile";
static void Main(string[] args)
{
ProcessStartInfo pInfo = new ProcessStartInfo(appString, argString);
pInfo.UseShellExecute = false;
pInfo.CreateNoWindow = true;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
pInfo.RedirectStandardInput = true;
pInfo.ErrorDialog = true;
Process process = new Process();
process.StartInfo = pInfo;
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.Exited += new EventHandler(process_Exited);
process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
Console.ReadKey();
}
static void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
}
static void process_Exited(object sender, EventArgs e)
{
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Received async output: " + e.Data);
}
}
正如我所说,Callas CLI的所有输出最终都会立即出现(检查大约需要35秒)。为了测试我自己的代码,我创建了一个小型控制台应用程序,以500毫秒的间隔输出数字10-20-30 -....- 100,它的输出完全显示在主应用程序中,随附间隔500毫秒。
有什么想法吗?
答案 0 :(得分:1)
你需要在没有缓冲的情况下冲洗stdouts和printfs,以便在控制台上打印时接收它们......
请参阅C equivalent of autoflush (flush stdout after each write)?
我有一天试图解决这个问题,以为我会在同样的情况下为其他人分享解决方案......
答案 1 :(得分:0)
我遇到了完全相同的问题,从C#.NET调用CLI应用程序,我需要从中获得实时stdOut更新(归档程序的进度报告),但它没有发送退出流程之前的任何输出(压缩10分钟后无法获得进度报告)
据我所知,这是进程的错误,而不是刷新stdOut缓冲区。我无法在 .Net 上找到任何方法来手动告诉进程刷新其stdOut。但是,我发现了一个hacky解决方案,能够实时读取stdOut。
我所做的就是访问工作的Process.StandardOutput.BaseStream.ReadByte()
方法,返回发送到stdOut的实际字节。这是纯粹的数据,包括马车操纵等。
然后将字节(似乎是ASCII?)转换为带有Char.ConvertFromUtf32(..)
的字符,并将字符推入stringBuilder
对象。现在,由于我有stdOut数据,我可以处理它,但我想要。
示例:对于我的情况,我想逐字捕获stdOut,然后回调给用户处理每个单词。
// - Standard process
proc = new Process();
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.filename = "...";
proc.start();
// - Init some things
int byte_r; // The byte that is going to be read from stdOut
StringBuilder word = new StringBuilder(); // Append the characters here
Action<String> onStdOutWord; // USERSET from before. Callbacks words read from stdOut
// As long as there is stdOut Data
while( (byte_r = proc.StandardOutput.BaseStream.ReadByte()) > -1 )
{
// If SPACE or ENTER callback the current word
if(byte_r==32 || byte_r==13) {
if(word.Length>0) {
onStdOutWord(word.ToString());
word.Clear();
}
}else{
// Append character to string, skip special characters
if(byte_r>32) {
word.Append(Char.ConvertFromUtf32(byte_r));
}
}//-
}// - end while
然后,使用自定义回调onStdOutWord()
我从CLI应用程序获取实时数据并处理它以获得我想要的内容。
上面的代码只是一个用例,因为我想获取文字,你可以改变它,让它像你想要的那样工作。 e.g。不分成单词并推出整个stringBuilder
对象?
我知道这是原始问题之后的8年,但我花了太多时间研究为什么我不能像OP一样获得标准数据,并且想要分享我的解决方案,以防其他人有同样的问题和绊倒在这个页面上,就像我做的那样