Reputation: 45
The code below works fine, except that for a few of the zip files I am getting the error
format error: can't find EOCD signature at C:/LegacyApp/perl/lib/Archive/Zip/Archive.pm line 695 Archive::Zip::Archive::_findEndOfCentralDirectory('Archive::Zip::Archive=HASH(0x375a730)', 'IO::File=GLOB(0x380eb90)') called at C:/LegacyApp/perl/lib/Archive/Zip/Archive.pm line 581 Archive::Zip::Archive::readFromFileHandle('Archive::Zip::Archive=HASH(0x375a730)', 'IO::File=GLOB(0x380eb90)',
Using below piece of code:
use Archive::Zip;
unzip($zipfile,$folder,$out);
sub unzip {
my ($archive, $want, $dir) = @_;
my $zip = Archive::Zip->new($archive);
foreach my $file ($zip->members) {
if (($file->fileName =~ /VERSION\/(.*?).cosipa.xlsx$/i)) {
$zip->extractMember($file,$dir.$file->fileName);
}
if ($file->fileName =~ /VERSION\/(.*?).txt$/i) {
$zip->extractMember($file,$dir.$file->fileName);
}
}
return 1;
}
If i delete that particular zip file, then it works fine. But I need a solution where i should be able to handle these zip files also and if possible please let me know what is wrong in code and zip files.
Thanks in advance
Upvotes: 2
Views: 3961
Reputation: 66964
It seems that some of those files indeed have format errors. Then you can catch those failures and handle the bad files (record names or delete files, etc), and otherwise proceed normally.
It isn't clear whether you are getting warnings (the program complains and continues) or errors (an issued die
is killing the program) and they need be handled differently.
If the shown "error" is an exception (the program dies) then you can trap and handle that
eval { unzip($zipfile, $folder, $out) };
if ($@) {
say "Error: $@";
# Interrogate. (Is it the expected error or some other?)
# handle it: record the filename for later/delete it/etc ...
}
# control returns here, unless the block above does exit/die/croak/goto
A builtin way to handle exceptions in Perl (the die) is via the block form of eval.† Also see the $@
error variable in perlvar.
If your message is a mere warning and the program continues then the eval
can't catch that, as it only traps exceptions, not warnings. One way to detect that is to install a SIG{__WARN__}
hook and in it throw a die
, and then the same eval
as above will work
# Block, so the change to how warnings work is scoped as tight as possible
HANDLE_ZIP: {
local $SIG{__WARN__} = sub {
die $_[0] if $_[0] =~ /^\s*format error:/; # raise exception, or
warn $_[0]; # re-emit the warning
};
eval { unzip($zipfile, $folder, $out) };
if ($@) {
# same as above...
}
};
Now if a warning comes out of unzip
, instead of it being printed the sub with reference assigned to $SIG{__WARN__}
is invoked. Then, if the warning's message is matched by that regex, a die
is thrown instead, with that message. Since this is triggered inside the eval
it is handled, as in the previous code sample. See %SIG
in perlvar.
Another way is to simply raise an exception in $SIG{__WARN__}
for any warning (only within this block!) and then deal with all details in the eval
's handler.
Note that local is important in all this, so that we don't change how warnings work across all code but only in this block.
This should work as it stands but please study the linked docs.
† There used to be subtle traps with handling $@
directly (prior to v5.14). While that had been resolved, all this is indeed low-level and it may be a good idea to consider using a module instead. That would wrap eval
+$@
for easier digestion, and perhaps for easier correct usage.
Upvotes: 2