LoginSignup
12
12

More than 5 years have passed since last update.

HTTPクライアントとStream::Bufferedの合わせ技

Posted at

鼻が詰まって困ってます

LWPFurlを使ってインターネットから様々なファイルをダウンロードする。よくやりますよね。その際に大きなファイルをGETしてしまい、perlのプロセスがメモリを大量に使い、OOM Killerに殺されて2年経つ、なんて経験をした人もきっと多いはず。

そこで使うのがレスポンスをファイルに書き出す技。Furlであれば

my $furl = Furl->new();
open my $fh, '>', $filename;
$furl->request(
    url => 'http://example.com/4k.jpg', 
    write_file => $fh
);
my $size = -s $fh;
seek($fh, 0, 0);

と書けて、$filenameのファイルに大きな画像データが保存されます。

しかし、取得対象とするデータが、大きなファイルから小さいファイルまでさまざまあり、また、取得しなければならない回数が多い場合、小さいファイルのためにいちいちテンポラリファイルを作るのももったいないと思う事もなきにしもあらず。

そんな場合に使いたいのがStream::Buffered

Stream::Bufferedはデータを一時的に格納するバッファとして使え、格納するサイズが大きくなると自動的にファイルに書き出してメモリを大量に使ってしまわないようになっています。元々Plack::TempBufferという名前でPlackに含まれ、PSGIサーバにおいてPOSTデータをバッファリングする用途に使われていましたが、切り出されて別のディストーションになっています。現在のPlack::TempBufferStream::Bufferedを継承するだけのモジュールとなっています。

Stream::Bufferedを使うと上のコードは、

my $furl = Furl->new();
my $buf = Stream::Buffered->new(メモリに格納するサイズ/デフォルト 1MB);
$furl->request(
    url => 'http://example.com/4k.jpg', 
    write_code => sub {
        my ( $status, $msg, $headers, $buf ) = @_;
        $buf->print($buf);
    }
);
my $size = $buf->size;
my $fh = $buf->rewind;

Stream::Bufferedのオブジェクトはファイルハンドルとしては扱えないので、write_fileではなく、write_codeを使います。そして最後にrewindメソッドを使うと、ファイル読み込みのオフセットをリセットした状態のファイルハンドルが得られます。seek済みなのでハマらなくてよいですね。$fhは通常のPerlのファイルハンドルなので、perlのread/sysread関数を使って読んだり、PSGIアプリケーションのレスポンスとしてそのまま使えます。

Stream::BufferedはHTTPクライアントで他のコンテンツを取って来て表示させる、Proxy的な動作を含むWebアプリケーションを作成する場合にぜひ使いたいモジュールです。

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12