2

我在cgi中有这个代码。

#!/usr/bin/perl
print "content-type: text/html \n\n";
print "<html><head><style>div.section{  font-size: 12px;    border: 1px solid #999999;  background-color: #EFEFEF;  margin: 10px 15px;}p{   font-family: \"Arial\", \"Helvetica\", \"sans-serif\";  font-size: 14px;    color: #444444; line-height: 30px;  margin: 0 10px 0 10px;  padding: 0 0 6px 0; text-align: center;}</style></head><body><div class=\"section\"><p>";
print "<b>YAMJ successfully updated your movie collection</b>";
print "</p><b>YAMJ output:</b><br>";
print "<pre>";
my $status = system("/usr/local/YAMJ/run.sh");
print "</pre>";
print "</div></body></html>";

该代码由 MissileHugger 为 Synology 中的 YAMJ 创建。问题是每次用户在 Synology 上打开应用程序时,它只会在脚本执行其操作时为您提供空白页面。

我想知道,有没有办法输出脚本当时正在做什么的实时数据进度?类似于我们安装 windows 软件时,有状态显示正在复制的文件等。

任何人都知道我是否可以做到这一点?

对不起我糟糕的英语。

4

3 回答 3

1

这是不容易做到的。原因:HTTP 是无状态的,浏览器只有在页面完全加载后才能呈现页面。

   SERVER     COMMUNICATION  CLIENT
+------------+              +---------+
| Rendezvous | <----------- | Client  |
+------------+  get status  | Browser |
    ^              via      |         |
    | write     javascript  |         |
    | status                |         |
+------------+    invoke    |         |
|   Process  | <----------- |         |
+------------+              +---------+

这是您可以使用的算法:

  1. 客户端浏览器请求 CGI 脚本。这开始了一个长期运行的过程。它向浏览器返回一个唯一的会话 ID

  2. 长时间运行的进程不断地用它的状态重写一个集合文件。

  3. 客户端浏览器轮询由 ID 指定的集合文件并使用 javascript 更新网页。HTML5 有一个你可以使用的<progress>仪表元素。

  4. 会话完成后,该文件将从服务器文件系统中删除。

在最简单的情况下,您将浏览器指向一个不断重写的页面,并指示浏览器每隔几秒钟重新加载该页面。在 AJAX 时代,这在很大程度上已经过时了。通过 javascript 加载内容会更流畅,并允许通过 JSON 传输复杂的状态,例如:

{ "status": 0.4,
  "comments": ["loaded a file",
               "doing really hard math",
               "almost finished - just a moment please"]}

而不是纯文本

Status: 40%

让服务器将这些事件推送到浏览器会非常优雅,但是,我不知道如何做到这一点。

于 2012-11-04T00:20:23.237 回答
0

很棒的代码。非常感谢您的解决方案。在过去的 3 天里,我一直在努力解决这个问题。

这就是我为更新您的脚本以适用于我的程序所做的工作。

#!/usr/bin/perl 

$|++; #This is needed to disable buffering

print "Content-type: text/html\r\n";
# This header is a quick hack which denies browser to gzip/deflate your output
print "Content-Encoding: plain\r\n\r\n";

&load_buffer; #This keeps the browser window printing progress as it's running

从这一点开始,任何打印语句都将在程序运行时工作。

示例测试

for ($i = 1; $i < 11; ++$i)

{
print "$i<br>\n";

sleep(1);

}

子程序

sub load_buffer {

my $cmd = qq{perl -e "\$|++; print ' ' and sleep(1) for 1..1"};
open my $PROC, '-|', $cmd or die $!;
while (sysread $PROC, $buffer, 1) { print "<!-- Browser shoudln't      cache this chunk ", ' ' x 1000, "-->\n";} 

}
于 2017-01-06T04:17:31.077 回答
-1

您需要启用STDOUT文件句柄的自动刷新来实现这一点。基本上,将以下代码放在脚本顶部(在 shebang 之后)应该会有所帮助:

$|++;

有关 autoflush的更多详细信息,请参阅之前关于 autoflush 问题的答案。

以下oneliners可以向您展示差异:

perl -e "$|++; print '#' and sleep(1) for 1..10"
perl -e "print '#' and sleep(1) for 1..10"

PS:我想如果你在终端运行 /usr/local/YAMJ/run.sh 你可以看到 ASCII 进度条。

升级版:

这应该可以工作,但是:

  1. 如果启用压缩,Apache 可能会压缩您的输出并作为单个块发送
  2. 浏览器(例如 Chrome)也缓冲输出。

因此,您必须执行一些额外的工作:

#!/usr/bin/perl

$|++;

print "Content-type: text/html\r\n";
# This header is a quick hack which denies browser to gzip/deflate your output
print "Content-Encoding: plain\r\n\r\n";

print "<html><body><h1>Start</h1>\n";

my $cmd = qq{perl -e "\$|++; print '#' and sleep(1) for 1..100"};

# Start your command, read it's output byte by byte
# and send it to the browser with a 1000+ bytes long comment
# which should prevent browser from caching just a single character
open my $PROC, '-|', $cmd or die $!;

my $buffer;
while (sysread $PROC, $buffer, 1) {
    print $buffer;
    print "<!-- Browser shoudln't cache this chunk ", ' ' x 1000, "-->\n";
}

print "<h1>Done</h1></body></html>";
于 2012-11-03T19:17:38.087 回答