in reply to
Unzipping files using Archive::Zip
The docs are a tad confusing, and it took me awhile
to figure them out, too.
Here are snippets of a script that works for me. It unzips
a zipfile of a known/expected name that contains one data file
of a known/expected name.
Disclaimer: Omitted is a lot of security stuff
that is necessary when accepting files via CGI and which is
beyond the scope of this subject. Also, since this
was written for a private website, there are assumptions made
here that might not be appropriate for general use. Finally,
be sure to use strict, and turn tainting on.
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use Fcntl qw(:DEFAULT :flock);
use Archive::Zip;
my $DataPath = '/MyDataDirectory';
# ...
my $query = new CGI;
my $InpFile = $query->param('filData');
# 'filData' is the name of the form's file selection
# control.
my $FileName = reverse($InpFile);
$FileName =~ s/\\.*//;
# Assuming a Windows client (\), strip client's path info
# from file name.
$FileName = reverse($FileName);
# There's GOTTA be a better way ...
$FileName eq 'zipfile.zip' or die 'Wrong file.';
undef $/;
my $ZipContents = <$FileName>;
$/ = "\n";
OpenLockTruncate (*ZIPFILE, "$DataPath/temp/zipfile.zip");
my $OldDefaultHandle = select(ZIPFILE);
$| = 1; # Force flush without closing/unlocking file.
select($OldDefaultHandle);
print ZIPFILE $ZipContents;
OpenLockTruncate (*INPFILE, "$DataPath/temp/unzipped.dat");
close INPFILE;
my $zip = Archive::Zip->new("$DataPath/temp/zipfile.zip");
my $file = ($zip->memberNames)[0];
$file eq 'unzipped.dat'
or die "Can't find correct data file in zip.";
$file = $zip->memberNamed($file)
or die "Can't access data file in zip.";
$file->extractToFileNamed("$DataPath/temp/unzipped.dat") == AZ_OK
or die "Unable to extract data file.";
# ...
sub OpenLockTruncate {
local *FH = $_[0];
sysopen (FH, $_[1], O_RDWR|O_CREAT, 0600)
or die "Can't open $_[1]: $!";
flock (FH, LOCK_EX) or die "Can't lock $_[1]: $!";
seek (FH, 0, 0) or die "Can't rewind $_[1]: $!";
truncate (FH, 0) or die "Can't truncate $_[1]: $!";
return 1;
}
As I reread this, I see there's possibly an issue with INPFILE
getting unlocked when it's closed, leaving unzipped.dat vulnerable
before it's extracted. I say "possibly" because ZIPFILE is
still locked. If it's used consistently throughout the
application as a semaphore file for unzipped.dat, there's
no problem. But I suspect this may be a bug in my app
that needs fixing. Once again, there's no shorter path to one's
own understanding of something than to explain it to others!