Archive for March, 2008

Archive::Zip reading from a scalar

Thursday, March 27th, 2008

Recently I spent a day trying to get Archive::Zip to open an existing zip file which was already loaded into memory (and stored in a scalar variable). It just wouldn’t work, so I tried the simplest possible example in a separate file, and it still wouldn’t work. Eventually I discovered a solution, though not perfect, it works for me, and I hope you can use it.

The simplest example of the functionality was included as one of the example files, readScalar.pl. This file uses IO::File to load a zip file into a scalar variable, and then uses IO::Scalar to open a seekable filehandle to that scalar which Archive::Zip can use with it’s readFromFileHandle method to load in the zip file.

Unfortunately, even this example wasn’t running on both my Mac OS X box or my CentOS box. I was getting an error that looked like this:

error: file not seekable
at /Library/Perl/5.8.6/Archive/Zip/Archive.pm line 437
Archive::Zip::Archive::readFromFileHandle('Archive::Zip::Archive=HASH(0x18e0700)', 'IO::Scalar=GLOB(0x1917dd8)') called at ./readScalar.pl line 20

After some Google searching, I came upon some bug reports on CPAN that dealt with this:

http://rt.cpan.org/Public/Bug/Display.html?id=7855
http://rt.cpan.org/Public/Bug/Display.html?id=7633

They both promised that the problem would be fixed in the next major release of Archive::Zip, but here we are, a couple years later, and it still doesn’t work.

The solution that worked for me was a combination of the user-submitted fixes. At the end of my perl script, I overrided Archive::Zip::Archive’s _isSeekable method with code suggested by NEDKONZ, and it looks like this:


# Override the _isSeekable function in Archive::Zip
no warnings 'redefine';
package Archive::Zip::Archive;
sub _isSeekable {
    my $fh = shift;
    if ( UNIVERSAL::isa( $fh, 'IO::Scalar' ) )
    {
        return $] >= 5.006;
    }
    elsif ( UNIVERSAL::isa( $fh, 'IO::String' ) )
    {
        return $] >= 5.006;
    }
    elsif ( UNIVERSAL::can( $fh, 'stat') )
    {
        return -f $fh;
    }
    return UNIVERSAL::can( $fh, 'seek');
}
use warnings 'all';

This function living in the bottom of my code ended the “error: file not seekable” problem. Of course, you could also just edit your Archive/Zip/Archive.pm file and fix it forever, rather than including this function in every file that needs to read a Zip file from memory.