end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転しました

IO::String + Archive::Zip でファイルを経由せずunzip

http://d.hatena.ne.jp/toku_bass/20110716#1310844437

Archive::Zipを使ってzipデータを解凍する場合、必ずファイルに書き出す必要があると思っていましたが、IO::String を使えば、zipデータを直接、解凍できるみたい。
※一方、圧縮(ZIP)に IO::String は必要なく、addString() , writeToFileNamed() でOK
http://search.cpan.org/perldoc?Archive%3A%3AZip
以下は、sample script

#!/usr/local/bin/perl
use strict;
use IO::String;
use Archive::Zip qw/:ERROR_CODES/;

my $UNZIP_DATA =<<EOF;
123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ
123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ
123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ123457890ABCDEFGHIJKLMNOPQRSTUVWXYZ
EOF
my $ZIP_FILE_NAME = 'archive_zip_test.zip';


main();

sub main {
    zip_data($UNZIP_DATA,$ZIP_FILE_NAME);
    unzip_data($ZIP_FILE_NAME);
}


sub zip_data {
    my ($unzip_data,$zip_file_name) = @_;
    my $zip = Archive::Zip->new();

    #ZIP(圧縮)
    $zip->addString( $unzip_data, 'UNZIP_TEST.TXT');
    unless ( $zip->writeToFileNamed($zip_file_name) == AZ_OK ) {
	die "can't writeToFileNamed( $zip_file_name ): $!";
    }
}

sub unzip_data {
    my ($zip_file_name) = @_;
    my $zip = Archive::Zip->new();

    my $zip_data;
    open(my $fh,"<",$zip_file_name) or die "can't open $zip_file_name : $!";
    {
	local $/ = undef;
	$zip_data = <$fh>;
    }
    close($fh) or die "can't close $zip_file_name : $!";

    unless($zip->readFromFileHandle(IO::String->new($zip_data)) == AZ_OK){
	die "can't readFromFileHandle(): $!";
    }
    #UNZIP(解凍)
    for my $member ($zip->members) {
	print $zip->extractMember($member);
    }
}

zip圧縮の結果を標準出力(STDOUT)へ出力

http://search.cpan.org/perldoc?Archive%3A%3AZip
cpanにも記載されていますが、具体的には「*STDOUT」をファイルハンドルとして利用することで可能です。

sub stdout_ziped_file {
    my ($self,$seikyu_no) = @_;

    my $m_zip = $self->zip_all_denpyo($seikyu_no);

    my $basename = join('_','請求書セット',$seikyu_no);
    print $self->get_http_download_header("$basename.ZIP",
					  "application/zip");

    my $stdout = *STDOUT;
    $m_zip->writeToFileHandle($stdout, 0);

    return $self;
}

sub zip_all_denpyo {
    my ($self,$seikyu_no) = @_;

    my $p_seikyu = Sing::PrintXls::Seikyu->new($self->{dbh},$self->{user});
    my $saved_dir = $p_seikyu->get_print_out_dir($seikyu_no,'NO_MAKE_DIR');

    my @denpyo_paths;
    for my $filename ( qw/kakeuri_seikyu.xls
			  kakeuri_seikyu_hikae.xls
			  kakeuri_seikyu_nyukin_hikae.xls/ ){
	my $denpyo_name = $p_seikyu->get_denpyo_name($filename);
	my $file_path = join('/',$saved_dir,$filename);
	next if (not -e $file_path);

	push(@denpyo_paths,[$file_path,$denpyo_name]);
    }

    return undef if @denpyo_paths == 0;

    my $zip = Archive::Zip->new();
    for my $denpyo_path ( @denpyo_paths ){
	$zip->addFile($denpyo_path->[0],encode('cp932',$denpyo_path->[1]));
    }

    return $zip;
}

その他関連エントリ

パスワード(暗号化)非対応なArchive::Zipは /usr/bin/zip, unzipで代用
http://d.hatena.ne.jp/end0tknr/20090214/1234567839
unzipの-p オプションでunzipの結果を標準出力へ
http://d.hatena.ne.jp/end0tknr/20110926/1317013278
zipファイルをhttp getし、zip解凍後、プレビュー表示
http://d.hatena.ne.jp/end0tknr/20110109/1294582088