【问题标题】:What is the easiest way in pure Perl to stream from another HTTP resource?纯 Perl 从另一个 HTTP 资源流式传输的最简单方法是什么?
【发布时间】:2009-10-14 17:46:37
【问题描述】:

在 Perl 中从另一个 HTTP 资源流式传输的最简单方法是什么(无需打开 shell 来 curl 并从标准输入中读取)?我在这里假设我正在读取的 HTTP 资源是一个潜在的无限流(或者只是非常非常长)

【问题讨论】:

    标签: perl http streaming mod-perl


    【解决方案1】:

    良好的旧 LWP 允许您将结果作为流处理。

    例如,这里是 yourFunc 的回调,读取/传递 byte_count 字节到每次调用 yourFunc (如果您不关心每次调用的数据有多大,并且只想将流处理为尽可能快):

    use LWP;
    ...
    $browser = LWP::UserAgent->new();
    $response = $browser->get($url, 
                              ':content_cb' => \&yourFunc, 
                              ':read_size_hint' => byte_count,);
    ...
    sub yourFunc {
       my($data, $response) = @_;
       # do your magic with $data
       # $respose will be a response object created once/if get() returns
    }
    

    【讨论】:

    • +1,这可能有效,我没有机会尝试它,因为在我有机会实现它之前,其他答案有效。
    • 哈,我知道了!我只是在文档中找不到它,所以我删除了我半途而废的答案:)
    • @Ether 我也不记得了,但请注意 LWP 和 LWP::Simple 是不同的野兽。
    【解决方案2】:

    HTTP::Lite 的request 方法允许您指定回调。

    $data_callback 参数(如果使用)是一种在接收数据时过滤数据或处理大量传输的方法。它必须是一个函数引用,并且将被传递:对发出回调的 http 请求实例的引用、对即将添加到主体的当前数据块的引用以及 $cbargs 参数(可能是任何东西)。它必须返回对要添加到文档正文中的数据的引用,或者返回 undef。

    但是,查看源代码,似乎在sub request 中存在一个错误,因为它似乎忽略了传递的回调。 使用set_callback似乎更安全:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use HTTP::Lite;
    
    my $http = HTTP::Lite->new;
    $http->set_callback(\&process_http_stream);
    $http->http11_mode(1);
    
    $http->request('http://www.example.com/');
    
    sub process_http_stream {
        my ($self, $phase, $dataref, $cbargs) = @_;
        warn $phase, "\n";
        return;
    }
    

    输出:

    C:\Temp> ht 连接 内容长度 完成标题 内容 内容完成 数据 完毕

    看起来传递给request方法的回调被不同地对待了:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use HTTP::Lite;
    
    my $http = HTTP::Lite->new;
    $http->http11_mode(1);
    
    my $count = 0;
    $http->request('http://www.example.com/',
        \&process_http_stream,
        \$count,
    );
    
    sub process_http_stream {
        my ($self, $data, $times) = @_;
        ++$$times;
        print "$$times====\n$$data\n===\n";
    }
    

    【讨论】:

    • 太棒了,这似乎可以解释为什么无论我在做什么,我返回的文档都是 0 字节。
    • 叹息似乎给予request 的回调函数的处理方式与使用set_callback 设置的回调函数不同,并且文档没有正确解释这一点。
    • LWP 也有回调机制。
    【解决方案3】:

    等等,我不明白。你为什么要排除一个单独的过程?这个:

    open my $stream, "-|", "curl $url" or die;
    while(<$stream>) { ... }
    

    对我来说,这确实是“最简单的方法”。这肯定比这里的其他建议更容易......

    【讨论】:

    • 我不确定这一点,但在 curl 读取完整响应之前不会阻止?
    • 不,curl 得到它时会吐出输出;它不会在内存中缓冲任何内容。您可以通过抓取一个大文件并观察 curl 加载时的进程大小来验证自己。
    • 最好不要创建线程,否则,这是一个很好的解决方案。
    • 除非您的机器上有多个千兆位可用带宽,否则在从网络资源中提取时,您将始终受到 I/O 限制。产生一个进程所涉及的 CPU 工作是无法测量的噪音。我强烈怀疑您过早地进行了优化。
    【解决方案4】:

    Event::Lib 将为您提供一个简单的接口,让您可以使用适合您平台的最快异步 IO 方法。

    IO::Lambda 也非常适合创建快速、响应迅速的 IO 应用程序。

    【讨论】:

    • 我不知道那个模块。看起来很棒!
    【解决方案5】:

    这是我最终通过 Net::HTTP 使用的版本

    这基本上是 Net::HTTP 手册页/perl 文档中示例的副本

    use Net::HTTP;
    
    my $s = Net::HTTP->new(Host => "www.example.com") || die $@;
    $s->write_request(GET => "/somestreamingdatasource.mp3");
    my ($code, $mess, %h) = $s->read_response_headers;
    while (1) {
      my $buf;
      my $n = $s->read_entity_body($buf, 4096);
      die "read failed: $!" unless defined $n;
      last unless $n;
      print STDERR "got $n bytes\n";
      print STDOUT $buf;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-25
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 2016-11-12
      • 2011-02-22
      相关资源
      最近更新 更多