Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Unzipping files using Archive::Zip

by Anonymous Monk
on Aug 11, 2001 at 00:02 UTC ( #104026=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing a script for people to upload .zip files to the server where I will need to "unzip" the files and process them. Does anyone have any experience using the Archive::Zip module? Will it do this for me? I've read through the docs for the module and they're a little confusing. Chad

Comment on Unzipping files using Archive::Zip
Re: Unzipping files using Archive::Zip
by Dr. Mu (Hermit) on Aug 11, 2001 at 12:03 UTC
    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!
Re: Unzipping files using Archive::Zip
by bikeNomad (Priest) on Aug 14, 2001 at 05:19 UTC
    What part of the Archive::Zip docs did you find confusing? I'm always looking to improve them.

    Assuming that your uploaded .zip file is sitting in a real file, it's pretty easy to use Archive::Zip. You do have to decide how you'll map directory names (since I'm assuming you don't want to clobber existing files with the same name on the server); you could, perhaps, just delete the directory path and stick the unzipped files somewhere. You might also want to provide some kind of semaphoring or other protection, but that isn't Archive::Zip's job, of course.

    Just extracting a zip file is easy. This quick example extracts a zip file, throwing away the directories and directory names (note that there is no error checking here):

    use Archive::Zip; my $zipname = 'whatever.zip'; my $destinationDirectory = '/some/where'; my $zip = Archive::Zip->new($zipname); foreach my $member ($zip->members) { next if $member->isDirectory; (my $extractName = $member->fileName) =~ s{.*/}{}; $member->extractToFileNamed( "$destinationDirectory/$extractName"); }
      I had tried your simple zip program. Your zip program is unzip all file in zip file. But how can I unzip certain fail in zip file?
        With the above example, you could see if the $member->fileName matches one of the file names you are interested in.
      Thank you for your post. It was very helpful. I'd like to reply to your question since the original poster did not. It is a couple of years later, but I think the situation is the same.

      Speaking for myself, I would say that the documentation is difficult because I did not find out how to do what I wanted within 1 minute, without reading any documentation.

      Let me explain: when I have a simple, standard problem, I begin by looking at the SYNOPSIS. If I don't find a match to my problem there, I start scanning the document, looking further on any interesting method names. I rarely read documentation from the start to the finish, unless I'm trying to do something obscure and I'm not sure how to go about it.

      In this case, I received a single zipped file from someone and I wanted to unzip it. After 15 minutes of looking through the documentation, I still didn't have my answer, so I came here, did a search, and found a solution. My resulting code is probably not as elegant as it could be, but it is functional:

      sub unzip { my ($archive, $want, $dir) = shift; my $zip = Archive::Zip->new($dir.$archive); foreach my $file ($zip->members) { next unless ($file->fileName eq $want); $file->extractToFileNamed($dir.$want); } croak "There was a problem extracting $want from $archive" unless +(-e $dir.$want); return 1; }

      So your solution here was clear and instantly solved my problem, whereas the documentation wasn't and didn't.

      My suggestion for improving the documentation would be to keep in mind that people are lazy and use cookbook style examples, like the one you gave here.

      If your synopsis consisted of two examples, one for creating an archive and one for extracting one (like the above example, but with error handling*), you'd probably address 90% of what people want to do with the module. For the curious, or for those whose problems are complicated, there's the whole wealth of your detailed documentation.

      You might also add some whitespace between examples in the synopsis and label them, for instance '# Unzip an archive' or '# Zip a directory'.

      * I find the error handling a bit unusual, with its comparisions instead of the more usual my $foo = dosomething() or croak "Couldn't dosomething: ".$Package::Errstr; or my $foo = $do->something() or croak "Couldn't something: ".$do->errstr();. This is why in my example I opted to do my own error checking, which is clearly inferior as it won't tell me why the operation failed.

        How do i create my own post here ? I dont find any way to do that.
Re: Unzipping files using Archive::Zip
by Anonymous Monk on Apr 14, 2008 at 13:22 UTC
    I have a problem, I can get the code to work all by itself, but if I make it a sub and pass in args for the zip file and destination it has an error: "Can't call method "members" on an undefined value at ./actionScript.pl line 1341" line 1341 if the foreach loop.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://104026]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (10)
As of 2014-08-22 22:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (168 votes), past polls