Perl (PSGI) で zip ファイルを動的に作りつつ順次クライアントに送りつける
滅多にないことだと思うが、非常に大きな zip ファイルを動的に生成してダウンロードさせるみたいなことをしたいことがあるかもしれない。
Archive::Zip だとストリーム生成できないので、Archive::Zip::SimpleZip を使う。Archive::Zip::SimpleZip だとストリーム出力で file handle などに書き出せる。
これで一度ファイルに書いてから、そのファイルを sendfile 的にレスポンスしてもいいのだけれど、書いている間はクライアントからしてみれば完全に無反応になるので、場合によってはタイムアウトになってしまうことがある。そこでストリーム出力をそのままクライアントにストリームしたくなる。
簡単なコードにすると以下のようになった。Archive::Zip::SimpleZip にコールバックかオブジェクトを渡せたらいいが、渡せないようなので、文字列リファレンスをいちいちクリアしながら出力するキモいコードになっている。
#!plackup
use strict;
use warnings;
use Archive::Zip::SimpleZip qw($SimpleZipError :zip_method);
sub {
my $env = shift;
# Return streaming response
sub {
my $respond = shift;
my $writer = $respond->([
200,
[
'Content-Type' => 'application/zip',
'Content-Disposition' => 'attachment; filename="dekai.zip"',
],
]);
my $_buf = "";
my $buf = \$_buf;
my $zip = Archive::Zip::SimpleZip->new($buf, Stream => 1) or die "Failed to create zip file";
for my $n (1..1000) {
warn $n;
$zip->addString("$n" x 100, Name => sprintf('%04d.txt', $n), Method => ZIP_CM_STORE) or die "SimpleZipError : $SimpleZipError";
$writer->write($$buf); $$buf = "";
select(undef, undef, undef, 0.01);
}
$zip->close or die "SimpleZipError : $SimpleZipError";
$writer->write($$buf);
$writer->close();
};
}
関連エントリー
- mbed USBSerial を WebUSB から扱うには mbed USBDevice ライブラリの中に USB CDC で動く USBSerial クラスが実装されている。これを Web USB ...
- microdata を適当に抜き出す Perl スクリプト あんまり仕様書読みこめてないけど書くだけ書いてみた。CPAN にあるのは依存がひどいし、別に HTML::Parser 的なもの一個の依存で...
- Google Fit の REST API で体重を自動入力する (画像は過去に入力したデータを全て Google Fit へ入力しなおした様子) Fit API 全体の概念 単純にグローバルな「体重」に対...
- Node.js の fs モジュールだけで GPIO の割込みを扱うには? 以下のようにすれば動くことが確認できた。 select や poll を明示的に呼び出すことができないが、edge を設定しさえすれば fs...
- SVGフォントのグリフを個別のSVG画像に変換する [tech] JavaScript の必要ないソーシャルボタン | Fri, Apr 15. 2016 - 氾濫原 これを作るとき、最初のう...