【问题标题】:Read realtime whole output in stream实时读取流中的整个输出
【发布时间】:2017-05-06 09:09:31
【问题描述】:

注意:这篇文章与post 不同,后者接受的答案只是一次阅读每一行。

我必须在服务器端对 3D 模型进行切片以进行 3D 打印,这个过程会花费一些时间。所以我要给用户展示流程,我用redis来存储流程。我想每 0.5 秒刷新一次进程。 比如sleep 0.5 sec,读取pip中的所有内容,每次处理。

目前我已经尝试了以下两个,第一个将一直保持到完成。第二次使用虽然不是正确的方法,它会一直写redis会导致客户端读取进程请求保持到最后。

这两个我都试过了:

第一个将一直保持到命令完成。

$descriptorspec = array(
    0 => array("pipe", "r"),
    1 => array("pipe", "w"),
    2 => array("pipe", "w")    //here curaengine log all the info into stderror
);
$command = './CuraEngine slice -p -j ' . $fdmprinterpath . ' -j ' . $configpath . ' -o ' . $gcodepath . ' -l ' . $tempstlpath;
$cwd = '/usr/local/curaengine';
$process = proc_open($command, $descriptorspec, $pipes, $cwd);
if(is_resource($process))
{
    print stream_get_contents($pipes[1]);  //This will hold until the command finished.
}

第二个实现为post 将每次一行。

  $descriptorspec = array(
        0 => array("pipe", "r"),
        1 => array("pipe", "w"),
        2 => array("pipe", "w")    //here curaengine log all the info into stderror
    );
    $command = './CuraEngine slice -p -j ' . $fdmprinterpath . ' -j ' . $configpath . ' -o ' . $gcodepath . ' -l ' . $tempstlpath;
    $cwd = '/usr/local/curaengine';
    $process = proc_open($command, $descriptorspec, $pipes, $cwd);
    if(is_resource($process))
    {
        while ($s = fgets($pipes[1])) {
            print $s;
            flush();
        }
    }

【问题讨论】:

  • 这是为了显示切片过程的某种进展吗?另外,您将使用什么客户端?浏览器?
  • @Ray Radin 是的,它是一个网络云服务,用户可以使用我的服务器对 3D 模型进行切片,生成用于 3D 打印的 gcode。第一个工具将在完成时输出所有内容。 secnod 实现将一直保持 printint 到最后。但正如问题所说,我想每 0.5 秒处理一次。
  • 我写了一个应用程序部署器,它更新一个进度条来显示部署进度。如果您愿意稍微修改您的方法,我可以向您展示它的精简版本作为示例。
  • 再想一想,这可能不是您这个问题的正确答案。
  • 你可以展示一下,因为没有人回答这个问题,我可能会从你所做的事情中学到一些东西。谢谢!

标签: php process stream pipe real-time


【解决方案1】:

您应该首先禁用输出缓冲,方法是将此行添加到代码的开头:

while(@ob_end_clean());

添加该行后,您的第二个代码应该能够流式传输。

不幸的是,即使禁用了输出缓冲,输出流的结果也取决于浏览器。 我测试了几种浏览器,包括桌面版和移动版, 并发现输出流在基于 Chromium 的浏览器中有效,但在 Firefox 中无效。

因为它依赖于浏览器,所以你使用的功能,无论是fgets() 还是fread(),都无关紧要。 如果你还需要流在其他浏览器上工作,你可以用 XMLHttpRequest 封装它。

这是一个适用于基于 Chromium 的浏览器的示例:

<?php
//stream.php

//disable output buffering
while(@ob_end_clean());
ob_implicit_flush(true);

if(empty($_SERVER['QUERY_STRING'])){
    //simulate a lengthy process
    file_put_contents(__FILE__.'.txt',$z='');
    for($i=0; $i<10; $i++){
        sleep(1);
        echo $s = "<div>{$i}</div>\n";
        file_put_contents(__FILE__.'.txt',$z.=$s);
    }
}else{
    //delay 500ms
    usleep(500000);
    echo file_get_contents(__FILE__.'.txt');
}

对应的 HTML 使其可以与其他浏览器一起使用:

<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8"></head>
<body>
<div id='stream'></div>
<script>
var active = true;
xhr('/stream.php',function(){ active = false; });
update();

function update(r){
    if(r) document.getElementById('stream').innerHTML = r.responseText;
    if(active) xhr('/stream.php?stream',update);
}

function xhr(url,callback){
    var r = new XMLHttpRequest();
    r.open('GET',url);
    r.onreadystatechange = function(){ if(r.readyState==4) callback(r); };
    r.send();
}
</script>
</body>
</html>

您可以使用相同的机制来创建我提到的进度条。

【讨论】:

    【解决方案2】:

    看看Symfony component Process。它提供了简单的异步流程和实时流程输出等。例如:

    $process = new Process($command);
    $process->start('processOutput');
    
    while ($process->isRunning()) {
        usleep(500000);
        $progress = $process->getIncrementalOutput();
        progressOutput($progress);
    }
    
    $output = $process->getOutput();
    
    //
    // Example implementations:
    //
    function progressOutput($progress) {        
        echo $progress;
        ob_flush();
    }
    
    function processOutput($type, $output) {
        if ($type == Process::OUT) {
            echo $output;
            ob_flush();
        }
    }
    

    【讨论】:

      【解决方案3】:

      使用fread() 替换fgets()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多